OLD | NEW |
| (Empty) |
1 # -*- coding: utf-8 -*- | |
2 # Copyright 2013 Google Inc. All Rights Reserved. | |
3 # | |
4 # Licensed under the Apache License, Version 2.0 (the "License"); | |
5 # you may not use this file except in compliance with the License. | |
6 # You may obtain a copy of the License at | |
7 # | |
8 # http://www.apache.org/licenses/LICENSE-2.0 | |
9 # | |
10 # Unless required by applicable law or agreed to in writing, software | |
11 # distributed under the License is distributed on an "AS IS" BASIS, | |
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 # See the License for the specific language governing permissions and | |
14 # limitations under the License. | |
15 """Integration tests for the acl command.""" | |
16 | |
17 from __future__ import absolute_import | |
18 | |
19 import re | |
20 | |
21 from gslib import aclhelpers | |
22 from gslib.command import CreateGsutilLogger | |
23 from gslib.cs_api_map import ApiSelector | |
24 from gslib.project_id import PopulateProjectId | |
25 from gslib.storage_url import StorageUrlFromString | |
26 import gslib.tests.testcase as testcase | |
27 from gslib.tests.testcase.integration_testcase import SkipForGS | |
28 from gslib.tests.testcase.integration_testcase import SkipForS3 | |
29 from gslib.tests.util import ObjectToURI as suri | |
30 from gslib.translation_helper import AclTranslation | |
31 from gslib.util import Retry | |
32 | |
33 PUBLIC_READ_JSON_ACL_TEXT = '"entity":"allUsers","role":"READER"' | |
34 | |
35 | |
36 class TestAclBase(testcase.GsUtilIntegrationTestCase): | |
37 """Integration test case base class for acl command.""" | |
38 | |
39 _set_acl_prefix = ['acl', 'set'] | |
40 _get_acl_prefix = ['acl', 'get'] | |
41 _set_defacl_prefix = ['defacl', 'set'] | |
42 _ch_acl_prefix = ['acl', 'ch'] | |
43 | |
44 _project_team = 'owners' | |
45 _project_test_acl = '%s-%s' % (_project_team, PopulateProjectId()) | |
46 | |
47 | |
48 @SkipForS3('Tests use GS ACL model.') | |
49 class TestAcl(TestAclBase): | |
50 """Integration tests for acl command.""" | |
51 | |
52 def setUp(self): | |
53 super(TestAcl, self).setUp() | |
54 self.sample_uri = self.CreateBucket() | |
55 self.sample_url = StorageUrlFromString(str(self.sample_uri)) | |
56 self.logger = CreateGsutilLogger('acl') | |
57 | |
58 def test_set_invalid_acl_object(self): | |
59 """Ensures that invalid content returns a bad request error.""" | |
60 obj_uri = suri(self.CreateObject(contents='foo')) | |
61 inpath = self.CreateTempFile(contents='badAcl') | |
62 stderr = self.RunGsUtil(self._set_acl_prefix + [inpath, obj_uri], | |
63 return_stderr=True, expected_status=1) | |
64 self.assertIn('ArgumentException', stderr) | |
65 | |
66 def test_set_invalid_acl_bucket(self): | |
67 """Ensures that invalid content returns a bad request error.""" | |
68 bucket_uri = suri(self.CreateBucket()) | |
69 inpath = self.CreateTempFile(contents='badAcl') | |
70 stderr = self.RunGsUtil(self._set_acl_prefix + [inpath, bucket_uri], | |
71 return_stderr=True, expected_status=1) | |
72 self.assertIn('ArgumentException', stderr) | |
73 | |
74 def test_set_xml_acl_json_api_object(self): | |
75 """Ensures XML content returns a bad request error and migration warning.""" | |
76 obj_uri = suri(self.CreateObject(contents='foo')) | |
77 inpath = self.CreateTempFile(contents='<ValidXml></ValidXml>') | |
78 stderr = self.RunGsUtil(self._set_acl_prefix + [inpath, obj_uri], | |
79 return_stderr=True, expected_status=1) | |
80 self.assertIn('ArgumentException', stderr) | |
81 self.assertIn('XML ACL data provided', stderr) | |
82 | |
83 def test_set_xml_acl_json_api_bucket(self): | |
84 """Ensures XML content returns a bad request error and migration warning.""" | |
85 bucket_uri = suri(self.CreateBucket()) | |
86 inpath = self.CreateTempFile(contents='<ValidXml></ValidXml>') | |
87 stderr = self.RunGsUtil(self._set_acl_prefix + [inpath, bucket_uri], | |
88 return_stderr=True, expected_status=1) | |
89 self.assertIn('ArgumentException', stderr) | |
90 self.assertIn('XML ACL data provided', stderr) | |
91 | |
92 def test_set_valid_acl_object(self): | |
93 """Tests setting a valid ACL on an object.""" | |
94 obj_uri = suri(self.CreateObject(contents='foo')) | |
95 acl_string = self.RunGsUtil(self._get_acl_prefix + [obj_uri], | |
96 return_stdout=True) | |
97 inpath = self.CreateTempFile(contents=acl_string) | |
98 self.RunGsUtil(self._set_acl_prefix + ['public-read', obj_uri]) | |
99 acl_string2 = self.RunGsUtil(self._get_acl_prefix + [obj_uri], | |
100 return_stdout=True) | |
101 self.RunGsUtil(self._set_acl_prefix + [inpath, obj_uri]) | |
102 acl_string3 = self.RunGsUtil(self._get_acl_prefix + [obj_uri], | |
103 return_stdout=True) | |
104 | |
105 self.assertNotEqual(acl_string, acl_string2) | |
106 self.assertEqual(acl_string, acl_string3) | |
107 | |
108 def test_set_valid_permission_whitespace_object(self): | |
109 """Ensures that whitespace is allowed in role and entity elements.""" | |
110 obj_uri = suri(self.CreateObject(contents='foo')) | |
111 acl_string = self.RunGsUtil(self._get_acl_prefix + [obj_uri], | |
112 return_stdout=True) | |
113 acl_string = re.sub(r'"role"', r'"role" \n', acl_string) | |
114 acl_string = re.sub(r'"entity"', r'\n "entity"', acl_string) | |
115 inpath = self.CreateTempFile(contents=acl_string) | |
116 | |
117 self.RunGsUtil(self._set_acl_prefix + [inpath, obj_uri]) | |
118 | |
119 def test_set_valid_acl_bucket(self): | |
120 """Ensures that valid canned and XML ACLs work with get/set.""" | |
121 bucket_uri = suri(self.CreateBucket()) | |
122 acl_string = self.RunGsUtil(self._get_acl_prefix + [bucket_uri], | |
123 return_stdout=True) | |
124 inpath = self.CreateTempFile(contents=acl_string) | |
125 self.RunGsUtil(self._set_acl_prefix + ['public-read', bucket_uri]) | |
126 acl_string2 = self.RunGsUtil(self._get_acl_prefix + [bucket_uri], | |
127 return_stdout=True) | |
128 self.RunGsUtil(self._set_acl_prefix + [inpath, bucket_uri]) | |
129 acl_string3 = self.RunGsUtil(self._get_acl_prefix + [bucket_uri], | |
130 return_stdout=True) | |
131 | |
132 self.assertNotEqual(acl_string, acl_string2) | |
133 self.assertEqual(acl_string, acl_string3) | |
134 | |
135 def test_invalid_canned_acl_object(self): | |
136 """Ensures that an invalid canned ACL returns a CommandException.""" | |
137 obj_uri = suri(self.CreateObject(contents='foo')) | |
138 stderr = self.RunGsUtil( | |
139 self._set_acl_prefix + ['not-a-canned-acl', obj_uri], | |
140 return_stderr=True, expected_status=1) | |
141 self.assertIn('CommandException', stderr) | |
142 self.assertIn('Invalid canned ACL', stderr) | |
143 | |
144 def test_set_valid_def_acl_bucket(self): | |
145 """Ensures that valid default canned and XML ACLs works with get/set.""" | |
146 bucket_uri = self.CreateBucket() | |
147 | |
148 # Default ACL is project private. | |
149 obj_uri1 = suri(self.CreateObject(bucket_uri=bucket_uri, contents='foo')) | |
150 acl_string = self.RunGsUtil(self._get_acl_prefix + [obj_uri1], | |
151 return_stdout=True) | |
152 | |
153 # Change it to authenticated-read. | |
154 self.RunGsUtil( | |
155 self._set_defacl_prefix + ['authenticated-read', suri(bucket_uri)]) | |
156 obj_uri2 = suri(self.CreateObject(bucket_uri=bucket_uri, contents='foo2')) | |
157 acl_string2 = self.RunGsUtil(self._get_acl_prefix + [obj_uri2], | |
158 return_stdout=True) | |
159 | |
160 # Now change it back to the default via XML. | |
161 inpath = self.CreateTempFile(contents=acl_string) | |
162 self.RunGsUtil(self._set_defacl_prefix + [inpath, suri(bucket_uri)]) | |
163 obj_uri3 = suri(self.CreateObject(bucket_uri=bucket_uri, contents='foo3')) | |
164 acl_string3 = self.RunGsUtil(self._get_acl_prefix + [obj_uri3], | |
165 return_stdout=True) | |
166 | |
167 self.assertNotEqual(acl_string, acl_string2) | |
168 self.assertIn('allAuthenticatedUsers', acl_string2) | |
169 self.assertEqual(acl_string, acl_string3) | |
170 | |
171 def test_acl_set_version_specific_uri(self): | |
172 """Tests setting an ACL on a specific version of an object.""" | |
173 bucket_uri = self.CreateVersionedBucket() | |
174 # Create initial object version. | |
175 uri = self.CreateObject(bucket_uri=bucket_uri, contents='data') | |
176 # Create a second object version. | |
177 inpath = self.CreateTempFile(contents='def') | |
178 self.RunGsUtil(['cp', inpath, uri.uri]) | |
179 | |
180 # Find out the two object version IDs. | |
181 lines = self.AssertNObjectsInBucket(bucket_uri, 2, versioned=True) | |
182 v0_uri_str, v1_uri_str = lines[0], lines[1] | |
183 | |
184 # Check that neither version currently has public-read permission | |
185 # (default ACL is project-private). | |
186 orig_acls = [] | |
187 for uri_str in (v0_uri_str, v1_uri_str): | |
188 acl = self.RunGsUtil(self._get_acl_prefix + [uri_str], | |
189 return_stdout=True) | |
190 self.assertNotIn(PUBLIC_READ_JSON_ACL_TEXT, | |
191 self._strip_json_whitespace(acl)) | |
192 orig_acls.append(acl) | |
193 | |
194 # Set the ACL for the older version of the object to public-read. | |
195 self.RunGsUtil(self._set_acl_prefix + ['public-read', v0_uri_str]) | |
196 # Check that the older version's ACL is public-read, but newer version | |
197 # is not. | |
198 acl = self.RunGsUtil(self._get_acl_prefix + [v0_uri_str], | |
199 return_stdout=True) | |
200 self.assertIn(PUBLIC_READ_JSON_ACL_TEXT, self._strip_json_whitespace(acl)) | |
201 acl = self.RunGsUtil(self._get_acl_prefix + [v1_uri_str], | |
202 return_stdout=True) | |
203 self.assertNotIn(PUBLIC_READ_JSON_ACL_TEXT, | |
204 self._strip_json_whitespace(acl)) | |
205 | |
206 # Check that reading the ACL with the version-less URI returns the | |
207 # original ACL (since the version-less URI means the current version). | |
208 acl = self.RunGsUtil(self._get_acl_prefix + [uri.uri], return_stdout=True) | |
209 self.assertEqual(acl, orig_acls[0]) | |
210 | |
211 def _strip_json_whitespace(self, json_text): | |
212 return re.sub(r'\s*', '', json_text) | |
213 | |
214 def testAclChangeWithUserId(self): | |
215 change = aclhelpers.AclChange(self.USER_TEST_ID + ':r', | |
216 scope_type=aclhelpers.ChangeType.USER) | |
217 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
218 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
219 self._AssertHas(acl, 'READER', 'UserById', self.USER_TEST_ID) | |
220 | |
221 def testAclChangeWithGroupId(self): | |
222 change = aclhelpers.AclChange(self.GROUP_TEST_ID + ':r', | |
223 scope_type=aclhelpers.ChangeType.GROUP) | |
224 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
225 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
226 self._AssertHas(acl, 'READER', 'GroupById', self.GROUP_TEST_ID) | |
227 | |
228 def testAclChangeWithUserEmail(self): | |
229 change = aclhelpers.AclChange(self.USER_TEST_ADDRESS + ':r', | |
230 scope_type=aclhelpers.ChangeType.USER) | |
231 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
232 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
233 self._AssertHas(acl, 'READER', 'UserByEmail', self.USER_TEST_ADDRESS) | |
234 | |
235 def testAclChangeWithGroupEmail(self): | |
236 change = aclhelpers.AclChange(self.GROUP_TEST_ADDRESS + ':fc', | |
237 scope_type=aclhelpers.ChangeType.GROUP) | |
238 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
239 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
240 self._AssertHas(acl, 'OWNER', 'GroupByEmail', self.GROUP_TEST_ADDRESS) | |
241 | |
242 def testAclChangeWithDomain(self): | |
243 change = aclhelpers.AclChange(self.DOMAIN_TEST + ':READ', | |
244 scope_type=aclhelpers.ChangeType.GROUP) | |
245 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
246 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
247 self._AssertHas(acl, 'READER', 'GroupByDomain', self.DOMAIN_TEST) | |
248 | |
249 def testAclChangeWithProjectOwners(self): | |
250 change = aclhelpers.AclChange(self._project_test_acl + ':READ', | |
251 scope_type=aclhelpers.ChangeType.PROJECT) | |
252 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
253 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
254 self._AssertHas(acl, 'READER', 'Project', self._project_test_acl) | |
255 | |
256 def testAclChangeWithAllUsers(self): | |
257 change = aclhelpers.AclChange('AllUsers:WRITE', | |
258 scope_type=aclhelpers.ChangeType.GROUP) | |
259 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
260 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
261 self._AssertHas(acl, 'WRITER', 'AllUsers') | |
262 | |
263 def testAclChangeWithAllAuthUsers(self): | |
264 change = aclhelpers.AclChange('AllAuthenticatedUsers:READ', | |
265 scope_type=aclhelpers.ChangeType.GROUP) | |
266 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
267 change.Execute(self.sample_url, acl, 'acl', self.logger) | |
268 self._AssertHas(acl, 'READER', 'AllAuthenticatedUsers') | |
269 remove = aclhelpers.AclDel('AllAuthenticatedUsers') | |
270 remove.Execute(self.sample_url, acl, 'acl', self.logger) | |
271 self._AssertHasNo(acl, 'READER', 'AllAuthenticatedUsers') | |
272 | |
273 def testAclDelWithUser(self): | |
274 add = aclhelpers.AclChange(self.USER_TEST_ADDRESS + ':READ', | |
275 scope_type=aclhelpers.ChangeType.USER) | |
276 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
277 add.Execute(self.sample_url, acl, 'acl', self.logger) | |
278 self._AssertHas(acl, 'READER', 'UserByEmail', self.USER_TEST_ADDRESS) | |
279 | |
280 remove = aclhelpers.AclDel(self.USER_TEST_ADDRESS) | |
281 remove.Execute(self.sample_url, acl, 'acl', self.logger) | |
282 self._AssertHasNo(acl, 'READ', 'UserByEmail', self.USER_TEST_ADDRESS) | |
283 | |
284 def testAclDelWithProjectOwners(self): | |
285 add = aclhelpers.AclChange(self._project_test_acl + ':READ', | |
286 scope_type=aclhelpers.ChangeType.PROJECT) | |
287 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
288 add.Execute(self.sample_url, acl, 'acl', self.logger) | |
289 self._AssertHas(acl, 'READER', 'Project', self._project_test_acl) | |
290 | |
291 remove = aclhelpers.AclDel(self._project_test_acl) | |
292 remove.Execute(self.sample_url, acl, 'acl', self.logger) | |
293 self._AssertHasNo(acl, 'READ', 'Project', self._project_test_acl) | |
294 | |
295 def testAclDelWithGroup(self): | |
296 add = aclhelpers.AclChange(self.USER_TEST_ADDRESS + ':READ', | |
297 scope_type=aclhelpers.ChangeType.GROUP) | |
298 acl = list(AclTranslation.BotoBucketAclToMessage(self.sample_uri.get_acl())) | |
299 add.Execute(self.sample_url, acl, 'acl', self.logger) | |
300 self._AssertHas(acl, 'READER', 'GroupByEmail', self.USER_TEST_ADDRESS) | |
301 | |
302 remove = aclhelpers.AclDel(self.USER_TEST_ADDRESS) | |
303 remove.Execute(self.sample_url, acl, 'acl', self.logger) | |
304 self._AssertHasNo(acl, 'READER', 'GroupByEmail', self.GROUP_TEST_ADDRESS) | |
305 | |
306 # | |
307 # Here are a whole lot of verbose asserts | |
308 # | |
309 | |
310 def _AssertHas(self, current_acl, perm, scope, value=None): | |
311 matches = list(self._YieldMatchingEntriesJson(current_acl, perm, scope, | |
312 value)) | |
313 self.assertEqual(1, len(matches)) | |
314 | |
315 def _AssertHasNo(self, current_acl, perm, scope, value=None): | |
316 matches = list(self._YieldMatchingEntriesJson(current_acl, perm, scope, | |
317 value)) | |
318 self.assertEqual(0, len(matches)) | |
319 | |
320 def _YieldMatchingEntriesJson(self, current_acl, perm, scope, value=None): | |
321 """Generator that yields entries that match the change descriptor. | |
322 | |
323 Args: | |
324 current_acl: A list of apitools_messages.BucketAccessControls or | |
325 ObjectAccessControls which will be searched for matching | |
326 entries. | |
327 perm: Role (permission) to match. | |
328 scope: Scope type to match. | |
329 value: Value to match (against the scope type). | |
330 | |
331 Yields: | |
332 An apitools_messages.BucketAccessControl or ObjectAccessControl. | |
333 """ | |
334 for entry in current_acl: | |
335 if (scope in ['UserById', 'GroupById'] and | |
336 entry.entityId and value == entry.entityId and | |
337 entry.role == perm): | |
338 yield entry | |
339 elif (scope in ['UserByEmail', 'GroupByEmail'] and | |
340 entry.email and value == entry.email and | |
341 entry.role == perm): | |
342 yield entry | |
343 elif (scope == 'GroupByDomain' and | |
344 entry.domain and value == entry.domain and | |
345 entry.role == perm): | |
346 yield entry | |
347 elif (scope == 'Project' and entry.role == perm and | |
348 value == entry.entityId): | |
349 yield entry | |
350 elif (scope in ['AllUsers', 'AllAuthenticatedUsers'] and | |
351 entry.entity.lower() == scope.lower() and | |
352 entry.role == perm): | |
353 yield entry | |
354 | |
355 def _MakeScopeRegex(self, role, entity_type, email_address): | |
356 template_regex = (r'\{.*"entity":\s*"%s-%s".*"role":\s*"%s".*\}' % | |
357 (entity_type, email_address, role)) | |
358 return re.compile(template_regex, flags=re.DOTALL) | |
359 | |
360 def _MakeProjectScopeRegex(self, role, project_team): | |
361 template_regex = (r'\{.*"entity":\s*"project-%s-\d+",\s*"projectTeam":\s*' | |
362 r'\{\s*"projectNumber":\s*"(\d+)",\s*"team":\s*"%s"\s*\},' | |
363 r'\s*"role":\s*"%s".*\}') % (project_team, project_team, | |
364 role) | |
365 | |
366 return re.compile(template_regex, flags=re.DOTALL) | |
367 | |
368 def testBucketAclChange(self): | |
369 """Tests acl change on a bucket.""" | |
370 test_regex = self._MakeScopeRegex( | |
371 'OWNER', 'user', self.USER_TEST_ADDRESS) | |
372 json_text = self.RunGsUtil( | |
373 self._get_acl_prefix + [suri(self.sample_uri)], return_stdout=True) | |
374 self.assertNotRegexpMatches(json_text, test_regex) | |
375 | |
376 self.RunGsUtil(self._ch_acl_prefix + | |
377 ['-u', self.USER_TEST_ADDRESS+':fc', suri(self.sample_uri)]) | |
378 json_text = self.RunGsUtil( | |
379 self._get_acl_prefix + [suri(self.sample_uri)], return_stdout=True) | |
380 self.assertRegexpMatches(json_text, test_regex) | |
381 | |
382 test_regex2 = self._MakeScopeRegex( | |
383 'WRITER', 'user', self.USER_TEST_ADDRESS) | |
384 self.RunGsUtil(self._ch_acl_prefix + | |
385 ['-u', self.USER_TEST_ADDRESS+':w', suri(self.sample_uri)]) | |
386 json_text2 = self.RunGsUtil( | |
387 self._get_acl_prefix + [suri(self.sample_uri)], return_stdout=True) | |
388 self.assertRegexpMatches(json_text2, test_regex2) | |
389 | |
390 self.RunGsUtil(self._ch_acl_prefix + | |
391 ['-d', self.USER_TEST_ADDRESS, suri(self.sample_uri)]) | |
392 | |
393 json_text3 = self.RunGsUtil( | |
394 self._get_acl_prefix + [suri(self.sample_uri)], return_stdout=True) | |
395 self.assertNotRegexpMatches(json_text3, test_regex) | |
396 | |
397 def testProjectAclChangesOnBucket(self): | |
398 """Tests project entity acl changes on a bucket.""" | |
399 | |
400 if self.test_api == ApiSelector.XML: | |
401 stderr = self.RunGsUtil(self._ch_acl_prefix + | |
402 ['-p', self._project_test_acl +':w', | |
403 suri(self.sample_uri)], | |
404 expected_status=1, | |
405 return_stderr=True) | |
406 self.assertIn(('CommandException: XML API does not support project' | |
407 ' scopes, cannot translate ACL.'), stderr) | |
408 else: | |
409 test_regex = self._MakeProjectScopeRegex( | |
410 'WRITER', self._project_team) | |
411 self.RunGsUtil(self._ch_acl_prefix + | |
412 ['-p', self._project_test_acl +':w', | |
413 suri(self.sample_uri)]) | |
414 json_text = self.RunGsUtil( | |
415 self._get_acl_prefix + [suri(self.sample_uri)], return_stdout=True) | |
416 | |
417 self.assertRegexpMatches(json_text, test_regex) | |
418 | |
419 # The api will accept string project ids, but stores the numeric project | |
420 # ids internally, this extracts the numeric id from the returned acls. | |
421 proj_num_id = test_regex.search(json_text).group(1) | |
422 acl_to_remove = '%s-%s' % (self._project_team, proj_num_id) | |
423 | |
424 self.RunGsUtil(self._ch_acl_prefix + | |
425 ['-d', acl_to_remove, suri(self.sample_uri)]) | |
426 | |
427 json_text2 = self.RunGsUtil( | |
428 self._get_acl_prefix + [suri(self.sample_uri)], return_stdout=True) | |
429 self.assertNotRegexpMatches(json_text2, test_regex) | |
430 | |
431 def testObjectAclChange(self): | |
432 """Tests acl change on an object.""" | |
433 obj = self.CreateObject(bucket_uri=self.sample_uri, contents='something') | |
434 self.AssertNObjectsInBucket(self.sample_uri, 1) | |
435 | |
436 test_regex = self._MakeScopeRegex( | |
437 'READER', 'group', self.GROUP_TEST_ADDRESS) | |
438 json_text = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
439 return_stdout=True) | |
440 self.assertNotRegexpMatches(json_text, test_regex) | |
441 | |
442 self.RunGsUtil(self._ch_acl_prefix + | |
443 ['-g', self.GROUP_TEST_ADDRESS+':READ', suri(obj)]) | |
444 json_text = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
445 return_stdout=True) | |
446 self.assertRegexpMatches(json_text, test_regex) | |
447 | |
448 test_regex2 = self._MakeScopeRegex( | |
449 'OWNER', 'group', self.GROUP_TEST_ADDRESS) | |
450 self.RunGsUtil(self._ch_acl_prefix + | |
451 ['-g', self.GROUP_TEST_ADDRESS+':OWNER', suri(obj)]) | |
452 json_text2 = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
453 return_stdout=True) | |
454 self.assertRegexpMatches(json_text2, test_regex2) | |
455 | |
456 self.RunGsUtil(self._ch_acl_prefix + | |
457 ['-d', self.GROUP_TEST_ADDRESS, suri(obj)]) | |
458 json_text3 = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
459 return_stdout=True) | |
460 self.assertNotRegexpMatches(json_text3, test_regex2) | |
461 | |
462 all_auth_regex = re.compile( | |
463 r'\{.*"entity":\s*"allAuthenticatedUsers".*"role":\s*"OWNER".*\}', | |
464 flags=re.DOTALL) | |
465 | |
466 self.RunGsUtil(self._ch_acl_prefix + ['-g', 'AllAuth:O', suri(obj)]) | |
467 json_text4 = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
468 return_stdout=True) | |
469 self.assertRegexpMatches(json_text4, all_auth_regex) | |
470 | |
471 def testObjectAclChangeAllUsers(self): | |
472 """Tests acl ch AllUsers:R on an object.""" | |
473 obj = self.CreateObject(bucket_uri=self.sample_uri, contents='something') | |
474 self.AssertNObjectsInBucket(self.sample_uri, 1) | |
475 | |
476 all_users_regex = re.compile( | |
477 r'\{.*"entity":\s*"allUsers".*"role":\s*"READER".*\}', flags=re.DOTALL) | |
478 json_text = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
479 return_stdout=True) | |
480 self.assertNotRegexpMatches(json_text, all_users_regex) | |
481 | |
482 self.RunGsUtil(self._ch_acl_prefix + | |
483 ['-g', 'AllUsers:R', suri(obj)]) | |
484 json_text = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
485 return_stdout=True) | |
486 self.assertRegexpMatches(json_text, all_users_regex) | |
487 | |
488 def testMultithreadedAclChange(self, count=10): | |
489 """Tests multi-threaded acl changing on several objects.""" | |
490 objects = [] | |
491 for i in range(count): | |
492 objects.append(self.CreateObject( | |
493 bucket_uri=self.sample_uri, | |
494 contents='something {0}'.format(i))) | |
495 | |
496 self.AssertNObjectsInBucket(self.sample_uri, count) | |
497 | |
498 test_regex = self._MakeScopeRegex( | |
499 'READER', 'group', self.GROUP_TEST_ADDRESS) | |
500 json_texts = [] | |
501 for obj in objects: | |
502 json_texts.append(self.RunGsUtil( | |
503 self._get_acl_prefix + [suri(obj)], return_stdout=True)) | |
504 for json_text in json_texts: | |
505 self.assertNotRegexpMatches(json_text, test_regex) | |
506 | |
507 uris = [suri(obj) for obj in objects] | |
508 self.RunGsUtil(['-m', '-DD'] + self._ch_acl_prefix + | |
509 ['-g', self.GROUP_TEST_ADDRESS+':READ'] + uris) | |
510 | |
511 json_texts = [] | |
512 for obj in objects: | |
513 json_texts.append(self.RunGsUtil( | |
514 self._get_acl_prefix + [suri(obj)], return_stdout=True)) | |
515 for json_text in json_texts: | |
516 self.assertRegexpMatches(json_text, test_regex) | |
517 | |
518 def testRecursiveChangeAcl(self): | |
519 """Tests recursively changing ACLs on nested objects.""" | |
520 obj = self.CreateObject(bucket_uri=self.sample_uri, object_name='foo/bar', | |
521 contents='something') | |
522 self.AssertNObjectsInBucket(self.sample_uri, 1) | |
523 | |
524 test_regex = self._MakeScopeRegex( | |
525 'READER', 'group', self.GROUP_TEST_ADDRESS) | |
526 json_text = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
527 return_stdout=True) | |
528 self.assertNotRegexpMatches(json_text, test_regex) | |
529 | |
530 @Retry(AssertionError, tries=5, timeout_secs=1) | |
531 def _AddAcl(): | |
532 self.RunGsUtil( | |
533 self._ch_acl_prefix + | |
534 ['-R', '-g', self.GROUP_TEST_ADDRESS+':READ', suri(obj)[:-3]]) | |
535 json_text = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
536 return_stdout=True) | |
537 self.assertRegexpMatches(json_text, test_regex) | |
538 _AddAcl() | |
539 | |
540 @Retry(AssertionError, tries=5, timeout_secs=1) | |
541 def _DeleteAcl(): | |
542 self.RunGsUtil(self._ch_acl_prefix + | |
543 ['-d', self.GROUP_TEST_ADDRESS, suri(obj)]) | |
544 json_text = self.RunGsUtil(self._get_acl_prefix + [suri(obj)], | |
545 return_stdout=True) | |
546 self.assertNotRegexpMatches(json_text, test_regex) | |
547 _DeleteAcl() | |
548 | |
549 def testMultiVersionSupport(self): | |
550 """Tests changing ACLs on multiple object versions.""" | |
551 bucket = self.CreateVersionedBucket() | |
552 object_name = self.MakeTempName('obj') | |
553 self.CreateObject( | |
554 bucket_uri=bucket, object_name=object_name, contents='One thing') | |
555 # Create another on the same URI, giving us a second version. | |
556 self.CreateObject( | |
557 bucket_uri=bucket, object_name=object_name, contents='Another thing') | |
558 | |
559 lines = self.AssertNObjectsInBucket(bucket, 2, versioned=True) | |
560 | |
561 obj_v1, obj_v2 = lines[0], lines[1] | |
562 | |
563 test_regex = self._MakeScopeRegex( | |
564 'READER', 'group', self.GROUP_TEST_ADDRESS) | |
565 json_text = self.RunGsUtil(self._get_acl_prefix + [obj_v1], | |
566 return_stdout=True) | |
567 self.assertNotRegexpMatches(json_text, test_regex) | |
568 | |
569 self.RunGsUtil(self._ch_acl_prefix + | |
570 ['-g', self.GROUP_TEST_ADDRESS+':READ', obj_v1]) | |
571 json_text = self.RunGsUtil(self._get_acl_prefix + [obj_v1], | |
572 return_stdout=True) | |
573 self.assertRegexpMatches(json_text, test_regex) | |
574 | |
575 json_text = self.RunGsUtil(self._get_acl_prefix + [obj_v2], | |
576 return_stdout=True) | |
577 self.assertNotRegexpMatches(json_text, test_regex) | |
578 | |
579 def testBadRequestAclChange(self): | |
580 stdout, stderr = self.RunGsUtil( | |
581 self._ch_acl_prefix + | |
582 ['-u', 'invalid_$$@hello.com:R', suri(self.sample_uri)], | |
583 return_stdout=True, return_stderr=True, expected_status=1) | |
584 self.assertIn('BadRequestException', stderr) | |
585 self.assertNotIn('Retrying', stdout) | |
586 self.assertNotIn('Retrying', stderr) | |
587 | |
588 def testAclGetWithoutFullControl(self): | |
589 object_uri = self.CreateObject(contents='foo') | |
590 with self.SetAnonymousBotoCreds(): | |
591 stderr = self.RunGsUtil(self._get_acl_prefix + [suri(object_uri)], | |
592 return_stderr=True, expected_status=1) | |
593 self.assertIn('AccessDeniedException', stderr) | |
594 | |
595 def testTooFewArgumentsFails(self): | |
596 """Tests calling ACL commands with insufficient number of arguments.""" | |
597 # No arguments for get, but valid subcommand. | |
598 stderr = self.RunGsUtil(self._get_acl_prefix, return_stderr=True, | |
599 expected_status=1) | |
600 self.assertIn('command requires at least', stderr) | |
601 | |
602 # No arguments for set, but valid subcommand. | |
603 stderr = self.RunGsUtil(self._set_acl_prefix, return_stderr=True, | |
604 expected_status=1) | |
605 self.assertIn('command requires at least', stderr) | |
606 | |
607 # No arguments for ch, but valid subcommand. | |
608 stderr = self.RunGsUtil(self._ch_acl_prefix, return_stderr=True, | |
609 expected_status=1) | |
610 self.assertIn('command requires at least', stderr) | |
611 | |
612 # Neither arguments nor subcommand. | |
613 stderr = self.RunGsUtil(['acl'], return_stderr=True, expected_status=1) | |
614 self.assertIn('command requires at least', stderr) | |
615 | |
616 def testMinusF(self): | |
617 """Tests -f option to continue after failure.""" | |
618 bucket_uri = self.CreateBucket() | |
619 obj_uri = suri(self.CreateObject(bucket_uri=bucket_uri, object_name='foo', | |
620 contents='foo')) | |
621 acl_string = self.RunGsUtil(self._get_acl_prefix + [obj_uri], | |
622 return_stdout=True) | |
623 self.RunGsUtil(self._set_acl_prefix + | |
624 ['-f', 'public-read', suri(bucket_uri) + 'foo2', obj_uri], | |
625 expected_status=1) | |
626 acl_string2 = self.RunGsUtil(self._get_acl_prefix + [obj_uri], | |
627 return_stdout=True) | |
628 | |
629 self.assertNotEqual(acl_string, acl_string2) | |
630 | |
631 | |
632 class TestS3CompatibleAcl(TestAclBase): | |
633 """ACL integration tests that work for s3 and gs URLs.""" | |
634 | |
635 def testAclObjectGetSet(self): | |
636 bucket_uri = self.CreateBucket() | |
637 obj_uri = self.CreateObject(bucket_uri=bucket_uri, contents='foo') | |
638 self.AssertNObjectsInBucket(bucket_uri, 1) | |
639 | |
640 stdout = self.RunGsUtil(self._get_acl_prefix + [suri(obj_uri)], | |
641 return_stdout=True) | |
642 set_contents = self.CreateTempFile(contents=stdout) | |
643 self.RunGsUtil(self._set_acl_prefix + [set_contents, suri(obj_uri)]) | |
644 | |
645 def testAclBucketGetSet(self): | |
646 bucket_uri = self.CreateBucket() | |
647 stdout = self.RunGsUtil(self._get_acl_prefix + [suri(bucket_uri)], | |
648 return_stdout=True) | |
649 set_contents = self.CreateTempFile(contents=stdout) | |
650 self.RunGsUtil(self._set_acl_prefix + [set_contents, suri(bucket_uri)]) | |
651 | |
652 | |
653 @SkipForGS('S3 ACLs accept XML and should not cause an XML warning.') | |
654 class TestS3OnlyAcl(TestAclBase): | |
655 """ACL integration tests that work only for s3 URLs.""" | |
656 | |
657 # TODO: Format all test case names consistently. | |
658 def test_set_xml_acl(self): | |
659 """Ensures XML content does not return an XML warning for S3.""" | |
660 obj_uri = suri(self.CreateObject(contents='foo')) | |
661 inpath = self.CreateTempFile(contents='<ValidXml></ValidXml>') | |
662 stderr = self.RunGsUtil(self._set_acl_prefix + [inpath, obj_uri], | |
663 return_stderr=True, expected_status=1) | |
664 self.assertIn('BadRequestException', stderr) | |
665 self.assertNotIn('XML ACL data provided', stderr) | |
666 | |
667 def test_set_xml_acl_bucket(self): | |
668 """Ensures XML content does not return an XML warning for S3.""" | |
669 bucket_uri = suri(self.CreateBucket()) | |
670 inpath = self.CreateTempFile(contents='<ValidXml></ValidXml>') | |
671 stderr = self.RunGsUtil(self._set_acl_prefix + [inpath, bucket_uri], | |
672 return_stderr=True, expected_status=1) | |
673 self.assertIn('BadRequestException', stderr) | |
674 self.assertNotIn('XML ACL data provided', stderr) | |
675 | |
676 | |
677 class TestAclOldAlias(TestAcl): | |
678 _set_acl_prefix = ['setacl'] | |
679 _get_acl_prefix = ['getacl'] | |
680 _set_defacl_prefix = ['setdefacl'] | |
681 _ch_acl_prefix = ['chacl'] | |
OLD | NEW |