| 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 |