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 |