Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/storage/acl.py: 41%

189 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:17 +0000

1# Copyright 2014 Google LLC 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""Manage access to objects and buckets.""" 

16 

17from google.cloud.storage._helpers import _add_generation_match_parameters 

18from google.cloud.storage.constants import _DEFAULT_TIMEOUT 

19from google.cloud.storage.retry import DEFAULT_RETRY 

20from google.cloud.storage.retry import DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED 

21 

22 

23class _ACLEntity(object): 

24 """Class representing a set of roles for an entity. 

25 

26 This is a helper class that you likely won't ever construct 

27 outside of using the factor methods on the :class:`ACL` object. 

28 

29 :type entity_type: str 

30 :param entity_type: The type of entity (ie, 'group' or 'user'). 

31 

32 :type identifier: str 

33 :param identifier: (Optional) The ID or e-mail of the entity. For the special 

34 entity types (like 'allUsers'). 

35 """ 

36 

37 READER_ROLE = "READER" 

38 WRITER_ROLE = "WRITER" 

39 OWNER_ROLE = "OWNER" 

40 

41 def __init__(self, entity_type, identifier=None): 

42 self.identifier = identifier 

43 self.roles = set([]) 

44 self.type = entity_type 

45 

46 def __str__(self): 

47 if not self.identifier: 

48 return str(self.type) 

49 else: 

50 return "{acl.type}-{acl.identifier}".format(acl=self) 

51 

52 def __repr__(self): 

53 return f"<ACL Entity: {self} ({', '.join(self.roles)})>" 

54 

55 def get_roles(self): 

56 """Get the list of roles permitted by this entity. 

57 

58 :rtype: list of strings 

59 :returns: The list of roles associated with this entity. 

60 """ 

61 return self.roles 

62 

63 def grant(self, role): 

64 """Add a role to the entity. 

65 

66 :type role: str 

67 :param role: The role to add to the entity. 

68 """ 

69 self.roles.add(role) 

70 

71 def revoke(self, role): 

72 """Remove a role from the entity. 

73 

74 :type role: str 

75 :param role: The role to remove from the entity. 

76 """ 

77 if role in self.roles: 

78 self.roles.remove(role) 

79 

80 def grant_read(self): 

81 """Grant read access to the current entity.""" 

82 self.grant(_ACLEntity.READER_ROLE) 

83 

84 def grant_write(self): 

85 """Grant write access to the current entity.""" 

86 self.grant(_ACLEntity.WRITER_ROLE) 

87 

88 def grant_owner(self): 

89 """Grant owner access to the current entity.""" 

90 self.grant(_ACLEntity.OWNER_ROLE) 

91 

92 def revoke_read(self): 

93 """Revoke read access from the current entity.""" 

94 self.revoke(_ACLEntity.READER_ROLE) 

95 

96 def revoke_write(self): 

97 """Revoke write access from the current entity.""" 

98 self.revoke(_ACLEntity.WRITER_ROLE) 

99 

100 def revoke_owner(self): 

101 """Revoke owner access from the current entity.""" 

102 self.revoke(_ACLEntity.OWNER_ROLE) 

103 

104 

105class ACL(object): 

106 """Container class representing a list of access controls.""" 

107 

108 _URL_PATH_ELEM = "acl" 

109 _PREDEFINED_QUERY_PARAM = "predefinedAcl" 

110 

111 PREDEFINED_XML_ACLS = { 

112 # XML API name -> JSON API name 

113 "project-private": "projectPrivate", 

114 "public-read": "publicRead", 

115 "public-read-write": "publicReadWrite", 

116 "authenticated-read": "authenticatedRead", 

117 "bucket-owner-read": "bucketOwnerRead", 

118 "bucket-owner-full-control": "bucketOwnerFullControl", 

119 } 

120 

121 PREDEFINED_JSON_ACLS = frozenset( 

122 [ 

123 "private", 

124 "projectPrivate", 

125 "publicRead", 

126 "publicReadWrite", 

127 "authenticatedRead", 

128 "bucketOwnerRead", 

129 "bucketOwnerFullControl", 

130 ] 

131 ) 

132 """See 

133 https://cloud.google.com/storage/docs/access-control/lists#predefined-acl 

134 """ 

135 

136 loaded = False 

137 

138 # Subclasses must override to provide these attributes (typically, 

139 # as properties). 

140 reload_path = None 

141 save_path = None 

142 user_project = None 

143 

144 def __init__(self): 

145 self.entities = {} 

146 

147 def _ensure_loaded(self, timeout=_DEFAULT_TIMEOUT): 

148 """Load if not already loaded. 

149 

150 :type timeout: float or tuple 

151 :param timeout: 

152 (Optional) The amount of time, in seconds, to wait 

153 for the server response. See: :ref:`configuring_timeouts` 

154 """ 

155 if not self.loaded: 

156 self.reload(timeout=timeout) 

157 

158 @classmethod 

159 def validate_predefined(cls, predefined): 

160 """Ensures predefined is in list of predefined json values 

161 

162 :type predefined: str 

163 :param predefined: name of a predefined acl 

164 

165 :type predefined: str 

166 :param predefined: validated JSON name of predefined acl 

167 

168 :raises: :exc: `ValueError`: If predefined is not a valid acl 

169 """ 

170 predefined = cls.PREDEFINED_XML_ACLS.get(predefined, predefined) 

171 if predefined and predefined not in cls.PREDEFINED_JSON_ACLS: 

172 raise ValueError(f"Invalid predefined ACL: {predefined}") 

173 return predefined 

174 

175 def reset(self): 

176 """Remove all entities from the ACL, and clear the ``loaded`` flag.""" 

177 self.entities.clear() 

178 self.loaded = False 

179 

180 def __iter__(self): 

181 self._ensure_loaded() 

182 

183 for entity in self.entities.values(): 

184 for role in entity.get_roles(): 

185 if role: 

186 yield {"entity": str(entity), "role": role} 

187 

188 def entity_from_dict(self, entity_dict): 

189 """Build an _ACLEntity object from a dictionary of data. 

190 

191 An entity is a mutable object that represents a list of roles 

192 belonging to either a user or group or the special types for all 

193 users and all authenticated users. 

194 

195 :type entity_dict: dict 

196 :param entity_dict: Dictionary full of data from an ACL lookup. 

197 

198 :rtype: :class:`_ACLEntity` 

199 :returns: An Entity constructed from the dictionary. 

200 """ 

201 entity = entity_dict["entity"] 

202 role = entity_dict["role"] 

203 

204 if entity == "allUsers": 

205 entity = self.all() 

206 

207 elif entity == "allAuthenticatedUsers": 

208 entity = self.all_authenticated() 

209 

210 elif "-" in entity: 

211 entity_type, identifier = entity.split("-", 1) 

212 entity = self.entity(entity_type=entity_type, identifier=identifier) 

213 

214 if not isinstance(entity, _ACLEntity): 

215 raise ValueError(f"Invalid dictionary: {entity_dict}") 

216 

217 entity.grant(role) 

218 return entity 

219 

220 def has_entity(self, entity): 

221 """Returns whether or not this ACL has any entries for an entity. 

222 

223 :type entity: :class:`_ACLEntity` 

224 :param entity: The entity to check for existence in this ACL. 

225 

226 :rtype: bool 

227 :returns: True of the entity exists in the ACL. 

228 """ 

229 self._ensure_loaded() 

230 return str(entity) in self.entities 

231 

232 def get_entity(self, entity, default=None): 

233 """Gets an entity object from the ACL. 

234 

235 :type entity: :class:`_ACLEntity` or string 

236 :param entity: The entity to get lookup in the ACL. 

237 

238 :type default: anything 

239 :param default: This value will be returned if the entity 

240 doesn't exist. 

241 

242 :rtype: :class:`_ACLEntity` 

243 :returns: The corresponding entity or the value provided 

244 to ``default``. 

245 """ 

246 self._ensure_loaded() 

247 return self.entities.get(str(entity), default) 

248 

249 def add_entity(self, entity): 

250 """Add an entity to the ACL. 

251 

252 :type entity: :class:`_ACLEntity` 

253 :param entity: The entity to add to this ACL. 

254 """ 

255 self._ensure_loaded() 

256 self.entities[str(entity)] = entity 

257 

258 def entity(self, entity_type, identifier=None): 

259 """Factory method for creating an Entity. 

260 

261 If an entity with the same type and identifier already exists, 

262 this will return a reference to that entity. If not, it will 

263 create a new one and add it to the list of known entities for 

264 this ACL. 

265 

266 :type entity_type: str 

267 :param entity_type: The type of entity to create 

268 (ie, ``user``, ``group``, etc) 

269 

270 :type identifier: str 

271 :param identifier: The ID of the entity (if applicable). 

272 This can be either an ID or an e-mail address. 

273 

274 :rtype: :class:`_ACLEntity` 

275 :returns: A new Entity or a reference to an existing identical entity. 

276 """ 

277 entity = _ACLEntity(entity_type=entity_type, identifier=identifier) 

278 if self.has_entity(entity): 

279 entity = self.get_entity(entity) 

280 else: 

281 self.add_entity(entity) 

282 return entity 

283 

284 def user(self, identifier): 

285 """Factory method for a user Entity. 

286 

287 :type identifier: str 

288 :param identifier: An id or e-mail for this particular user. 

289 

290 :rtype: :class:`_ACLEntity` 

291 :returns: An Entity corresponding to this user. 

292 """ 

293 return self.entity("user", identifier=identifier) 

294 

295 def group(self, identifier): 

296 """Factory method for a group Entity. 

297 

298 :type identifier: str 

299 :param identifier: An id or e-mail for this particular group. 

300 

301 :rtype: :class:`_ACLEntity` 

302 :returns: An Entity corresponding to this group. 

303 """ 

304 return self.entity("group", identifier=identifier) 

305 

306 def domain(self, domain): 

307 """Factory method for a domain Entity. 

308 

309 :type domain: str 

310 :param domain: The domain for this entity. 

311 

312 :rtype: :class:`_ACLEntity` 

313 :returns: An entity corresponding to this domain. 

314 """ 

315 return self.entity("domain", identifier=domain) 

316 

317 def all(self): 

318 """Factory method for an Entity representing all users. 

319 

320 :rtype: :class:`_ACLEntity` 

321 :returns: An entity representing all users. 

322 """ 

323 return self.entity("allUsers") 

324 

325 def all_authenticated(self): 

326 """Factory method for an Entity representing all authenticated users. 

327 

328 :rtype: :class:`_ACLEntity` 

329 :returns: An entity representing all authenticated users. 

330 """ 

331 return self.entity("allAuthenticatedUsers") 

332 

333 def get_entities(self): 

334 """Get a list of all Entity objects. 

335 

336 :rtype: list of :class:`_ACLEntity` objects 

337 :returns: A list of all Entity objects. 

338 """ 

339 self._ensure_loaded() 

340 return list(self.entities.values()) 

341 

342 @property 

343 def client(self): 

344 """Abstract getter for the object client.""" 

345 raise NotImplementedError 

346 

347 def _require_client(self, client): 

348 """Check client or verify over-ride. 

349 

350 :type client: :class:`~google.cloud.storage.client.Client` or 

351 ``NoneType`` 

352 :param client: the client to use. If not passed, falls back to the 

353 ``client`` stored on the current ACL. 

354 

355 :rtype: :class:`google.cloud.storage.client.Client` 

356 :returns: The client passed in or the currently bound client. 

357 """ 

358 if client is None: 

359 client = self.client 

360 return client 

361 

362 def reload(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY): 

363 """Reload the ACL data from Cloud Storage. 

364 

365 If :attr:`user_project` is set, bills the API request to that project. 

366 

367 :type client: :class:`~google.cloud.storage.client.Client` or 

368 ``NoneType`` 

369 :param client: (Optional) The client to use. If not passed, falls back 

370 to the ``client`` stored on the ACL's parent. 

371 :type timeout: float or tuple 

372 :param timeout: 

373 (Optional) The amount of time, in seconds, to wait 

374 for the server response. See: :ref:`configuring_timeouts` 

375 

376 :type retry: :class:`~google.api_core.retry.Retry` 

377 :param retry: 

378 (Optional) How to retry the RPC. See: :ref:`configuring_retries` 

379 """ 

380 path = self.reload_path 

381 client = self._require_client(client) 

382 query_params = {} 

383 

384 if self.user_project is not None: 

385 query_params["userProject"] = self.user_project 

386 

387 self.entities.clear() 

388 

389 found = client._get_resource( 

390 path, 

391 query_params=query_params, 

392 timeout=timeout, 

393 retry=retry, 

394 ) 

395 self.loaded = True 

396 

397 for entry in found.get("items", ()): 

398 self.add_entity(self.entity_from_dict(entry)) 

399 

400 def _save( 

401 self, 

402 acl, 

403 predefined, 

404 client, 

405 if_generation_match=None, 

406 if_generation_not_match=None, 

407 if_metageneration_match=None, 

408 if_metageneration_not_match=None, 

409 timeout=_DEFAULT_TIMEOUT, 

410 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

411 ): 

412 """Helper for :meth:`save` and :meth:`save_predefined`. 

413 

414 :type acl: :class:`google.cloud.storage.acl.ACL`, or a compatible list. 

415 :param acl: The ACL object to save. If left blank, this will save 

416 current entries. 

417 

418 :type predefined: str 

419 :param predefined: An identifier for a predefined ACL. Must be one of the 

420 keys in :attr:`PREDEFINED_JSON_ACLS` If passed, `acl` must be None. 

421 

422 :type client: :class:`~google.cloud.storage.client.Client` or 

423 ``NoneType`` 

424 :param client: (Optional) The client to use. If not passed, falls back 

425 to the ``client`` stored on the ACL's parent. 

426 

427 :type if_generation_match: long 

428 :param if_generation_match: 

429 (Optional) See :ref:`using-if-generation-match` 

430 

431 :type if_generation_not_match: long 

432 :param if_generation_not_match: 

433 (Optional) See :ref:`using-if-generation-not-match` 

434 

435 :type if_metageneration_match: long 

436 :param if_metageneration_match: 

437 (Optional) See :ref:`using-if-metageneration-match` 

438 

439 :type if_metageneration_not_match: long 

440 :param if_metageneration_not_match: 

441 (Optional) See :ref:`using-if-metageneration-not-match` 

442 

443 :type timeout: float or tuple 

444 :param timeout: 

445 (Optional) The amount of time, in seconds, to wait 

446 for the server response. See: :ref:`configuring_timeouts` 

447 

448 :type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy 

449 :param retry: 

450 (Optional) How to retry the RPC. See: :ref:`configuring_retries` 

451 """ 

452 client = self._require_client(client) 

453 query_params = {"projection": "full"} 

454 

455 if predefined is not None: 

456 acl = [] 

457 query_params[self._PREDEFINED_QUERY_PARAM] = predefined 

458 

459 if self.user_project is not None: 

460 query_params["userProject"] = self.user_project 

461 

462 _add_generation_match_parameters( 

463 query_params, 

464 if_generation_match=if_generation_match, 

465 if_generation_not_match=if_generation_not_match, 

466 if_metageneration_match=if_metageneration_match, 

467 if_metageneration_not_match=if_metageneration_not_match, 

468 ) 

469 

470 path = self.save_path 

471 

472 result = client._patch_resource( 

473 path, 

474 {self._URL_PATH_ELEM: list(acl)}, 

475 query_params=query_params, 

476 timeout=timeout, 

477 retry=retry, 

478 ) 

479 

480 self.entities.clear() 

481 

482 for entry in result.get(self._URL_PATH_ELEM, ()): 

483 self.add_entity(self.entity_from_dict(entry)) 

484 

485 self.loaded = True 

486 

487 def save( 

488 self, 

489 acl=None, 

490 client=None, 

491 if_generation_match=None, 

492 if_generation_not_match=None, 

493 if_metageneration_match=None, 

494 if_metageneration_not_match=None, 

495 timeout=_DEFAULT_TIMEOUT, 

496 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

497 ): 

498 """Save this ACL for the current bucket. 

499 

500 If :attr:`user_project` is set, bills the API request to that project. 

501 

502 :type acl: :class:`google.cloud.storage.acl.ACL`, or a compatible list. 

503 :param acl: The ACL object to save. If left blank, this will save 

504 current entries. 

505 

506 :type client: :class:`~google.cloud.storage.client.Client` or 

507 ``NoneType`` 

508 :param client: (Optional) The client to use. If not passed, falls back 

509 to the ``client`` stored on the ACL's parent. 

510 

511 :type if_generation_match: long 

512 :param if_generation_match: 

513 (Optional) See :ref:`using-if-generation-match` 

514 

515 :type if_generation_not_match: long 

516 :param if_generation_not_match: 

517 (Optional) See :ref:`using-if-generation-not-match` 

518 

519 :type if_metageneration_match: long 

520 :param if_metageneration_match: 

521 (Optional) See :ref:`using-if-metageneration-match` 

522 

523 :type if_metageneration_not_match: long 

524 :param if_metageneration_not_match: 

525 (Optional) See :ref:`using-if-metageneration-not-match` 

526 

527 :type timeout: float or tuple 

528 :param timeout: 

529 (Optional) The amount of time, in seconds, to wait 

530 for the server response. See: :ref:`configuring_timeouts` 

531 

532 :type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy 

533 :param retry: 

534 (Optional) How to retry the RPC. See: :ref:`configuring_retries` 

535 """ 

536 if acl is None: 

537 acl = self 

538 save_to_backend = acl.loaded 

539 else: 

540 save_to_backend = True 

541 

542 if save_to_backend: 

543 self._save( 

544 acl, 

545 None, 

546 client, 

547 if_generation_match=if_generation_match, 

548 if_generation_not_match=if_generation_not_match, 

549 if_metageneration_match=if_metageneration_match, 

550 if_metageneration_not_match=if_metageneration_not_match, 

551 timeout=timeout, 

552 retry=retry, 

553 ) 

554 

555 def save_predefined( 

556 self, 

557 predefined, 

558 client=None, 

559 if_generation_match=None, 

560 if_generation_not_match=None, 

561 if_metageneration_match=None, 

562 if_metageneration_not_match=None, 

563 timeout=_DEFAULT_TIMEOUT, 

564 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

565 ): 

566 """Save this ACL for the current bucket using a predefined ACL. 

567 

568 If :attr:`user_project` is set, bills the API request to that project. 

569 

570 :type predefined: str 

571 :param predefined: An identifier for a predefined ACL. Must be one 

572 of the keys in :attr:`PREDEFINED_JSON_ACLS` 

573 or :attr:`PREDEFINED_XML_ACLS` (which will be 

574 aliased to the corresponding JSON name). 

575 If passed, `acl` must be None. 

576 

577 :type client: :class:`~google.cloud.storage.client.Client` or 

578 ``NoneType`` 

579 :param client: (Optional) The client to use. If not passed, falls back 

580 to the ``client`` stored on the ACL's parent. 

581 

582 :type if_generation_match: long 

583 :param if_generation_match: 

584 (Optional) See :ref:`using-if-generation-match` 

585 

586 :type if_generation_not_match: long 

587 :param if_generation_not_match: 

588 (Optional) See :ref:`using-if-generation-not-match` 

589 

590 :type if_metageneration_match: long 

591 :param if_metageneration_match: 

592 (Optional) See :ref:`using-if-metageneration-match` 

593 

594 :type if_metageneration_not_match: long 

595 :param if_metageneration_not_match: 

596 (Optional) See :ref:`using-if-metageneration-not-match` 

597 

598 :type timeout: float or tuple 

599 :param timeout: 

600 (Optional) The amount of time, in seconds, to wait 

601 for the server response. See: :ref:`configuring_timeouts` 

602 

603 :type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy 

604 :param retry: 

605 (Optional) How to retry the RPC. See: :ref:`configuring_retries` 

606 """ 

607 predefined = self.validate_predefined(predefined) 

608 self._save( 

609 None, 

610 predefined, 

611 client, 

612 if_generation_match=if_generation_match, 

613 if_generation_not_match=if_generation_not_match, 

614 if_metageneration_match=if_metageneration_match, 

615 if_metageneration_not_match=if_metageneration_not_match, 

616 timeout=timeout, 

617 retry=retry, 

618 ) 

619 

620 def clear( 

621 self, 

622 client=None, 

623 if_generation_match=None, 

624 if_generation_not_match=None, 

625 if_metageneration_match=None, 

626 if_metageneration_not_match=None, 

627 timeout=_DEFAULT_TIMEOUT, 

628 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

629 ): 

630 """Remove all ACL entries. 

631 

632 If :attr:`user_project` is set, bills the API request to that project. 

633 

634 Note that this won't actually remove *ALL* the rules, but it 

635 will remove all the non-default rules. In short, you'll still 

636 have access to a bucket that you created even after you clear 

637 ACL rules with this method. 

638 

639 :type client: :class:`~google.cloud.storage.client.Client` or 

640 ``NoneType`` 

641 :param client: (Optional) The client to use. If not passed, falls back 

642 to the ``client`` stored on the ACL's parent. 

643 

644 :type if_generation_match: long 

645 :param if_generation_match: 

646 (Optional) See :ref:`using-if-generation-match` 

647 

648 :type if_generation_not_match: long 

649 :param if_generation_not_match: 

650 (Optional) See :ref:`using-if-generation-not-match` 

651 

652 :type if_metageneration_match: long 

653 :param if_metageneration_match: 

654 (Optional) See :ref:`using-if-metageneration-match` 

655 

656 :type if_metageneration_not_match: long 

657 :param if_metageneration_not_match: 

658 (Optional) See :ref:`using-if-metageneration-not-match` 

659 

660 :type timeout: float or tuple 

661 :param timeout: 

662 (Optional) The amount of time, in seconds, to wait 

663 for the server response. See: :ref:`configuring_timeouts` 

664 

665 :type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy 

666 :param retry: 

667 (Optional) How to retry the RPC. See: :ref:`configuring_retries` 

668 """ 

669 self.save( 

670 [], 

671 client=client, 

672 if_generation_match=if_generation_match, 

673 if_generation_not_match=if_generation_not_match, 

674 if_metageneration_match=if_metageneration_match, 

675 if_metageneration_not_match=if_metageneration_not_match, 

676 timeout=timeout, 

677 retry=retry, 

678 ) 

679 

680 

681class BucketACL(ACL): 

682 """An ACL specifically for a bucket. 

683 

684 :type bucket: :class:`google.cloud.storage.bucket.Bucket` 

685 :param bucket: The bucket to which this ACL relates. 

686 """ 

687 

688 def __init__(self, bucket): 

689 super(BucketACL, self).__init__() 

690 self.bucket = bucket 

691 

692 @property 

693 def client(self): 

694 """The client bound to this ACL's bucket.""" 

695 return self.bucket.client 

696 

697 @property 

698 def reload_path(self): 

699 """Compute the path for GET API requests for this ACL.""" 

700 return f"{self.bucket.path}/{self._URL_PATH_ELEM}" 

701 

702 @property 

703 def save_path(self): 

704 """Compute the path for PATCH API requests for this ACL.""" 

705 return self.bucket.path 

706 

707 @property 

708 def user_project(self): 

709 """Compute the user project charged for API requests for this ACL.""" 

710 return self.bucket.user_project 

711 

712 

713class DefaultObjectACL(BucketACL): 

714 """A class representing the default object ACL for a bucket.""" 

715 

716 _URL_PATH_ELEM = "defaultObjectAcl" 

717 _PREDEFINED_QUERY_PARAM = "predefinedDefaultObjectAcl" 

718 

719 

720class ObjectACL(ACL): 

721 """An ACL specifically for a Cloud Storage object / blob. 

722 

723 :type blob: :class:`google.cloud.storage.blob.Blob` 

724 :param blob: The blob that this ACL corresponds to. 

725 """ 

726 

727 def __init__(self, blob): 

728 super(ObjectACL, self).__init__() 

729 self.blob = blob 

730 

731 @property 

732 def client(self): 

733 """The client bound to this ACL's blob.""" 

734 return self.blob.client 

735 

736 @property 

737 def reload_path(self): 

738 """Compute the path for GET API requests for this ACL.""" 

739 return f"{self.blob.path}/acl" 

740 

741 @property 

742 def save_path(self): 

743 """Compute the path for PATCH API requests for this ACL.""" 

744 return self.blob.path 

745 

746 @property 

747 def user_project(self): 

748 """Compute the user project charged for API requests for this ACL.""" 

749 return self.blob.user_project