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

190 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 07:13 +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 client = None 

141 reload_path = None 

142 save_path = None 

143 user_project = None 

144 

145 def __init__(self): 

146 self.entities = {} 

147 

148 def _ensure_loaded(self, timeout=_DEFAULT_TIMEOUT): 

149 """Load if not already loaded. 

150 

151 :type timeout: float or tuple 

152 :param timeout: 

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

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

155 """ 

156 if not self.loaded: 

157 self.reload(timeout=timeout) 

158 

159 @classmethod 

160 def validate_predefined(cls, predefined): 

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

162 

163 :type predefined: str 

164 :param predefined: name of a predefined acl 

165 

166 :type predefined: str 

167 :param predefined: validated JSON name of predefined acl 

168 

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

170 """ 

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

172 if predefined and predefined not in cls.PREDEFINED_JSON_ACLS: 

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

174 return predefined 

175 

176 def reset(self): 

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

178 self.entities.clear() 

179 self.loaded = False 

180 

181 def __iter__(self): 

182 self._ensure_loaded() 

183 

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

185 for role in entity.get_roles(): 

186 if role: 

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

188 

189 def entity_from_dict(self, entity_dict): 

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

191 

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

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

194 users and all authenticated users. 

195 

196 :type entity_dict: dict 

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

198 

199 :rtype: :class:`_ACLEntity` 

200 :returns: An Entity constructed from the dictionary. 

201 """ 

202 entity = entity_dict["entity"] 

203 role = entity_dict["role"] 

204 

205 if entity == "allUsers": 

206 entity = self.all() 

207 

208 elif entity == "allAuthenticatedUsers": 

209 entity = self.all_authenticated() 

210 

211 elif "-" in entity: 

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

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

214 

215 if not isinstance(entity, _ACLEntity): 

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

217 

218 entity.grant(role) 

219 return entity 

220 

221 def has_entity(self, entity): 

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

223 

224 :type entity: :class:`_ACLEntity` 

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

226 

227 :rtype: bool 

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

229 """ 

230 self._ensure_loaded() 

231 return str(entity) in self.entities 

232 

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

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

235 

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

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

238 

239 :type default: anything 

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

241 doesn't exist. 

242 

243 :rtype: :class:`_ACLEntity` 

244 :returns: The corresponding entity or the value provided 

245 to ``default``. 

246 """ 

247 self._ensure_loaded() 

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

249 

250 def add_entity(self, entity): 

251 """Add an entity to the ACL. 

252 

253 :type entity: :class:`_ACLEntity` 

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

255 """ 

256 self._ensure_loaded() 

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

258 

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

260 """Factory method for creating an Entity. 

261 

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

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

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

265 this ACL. 

266 

267 :type entity_type: str 

268 :param entity_type: The type of entity to create 

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

270 

271 :type identifier: str 

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

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

274 

275 :rtype: :class:`_ACLEntity` 

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

277 """ 

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

279 if self.has_entity(entity): 

280 entity = self.get_entity(entity) 

281 else: 

282 self.add_entity(entity) 

283 return entity 

284 

285 def user(self, identifier): 

286 """Factory method for a user Entity. 

287 

288 :type identifier: str 

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

290 

291 :rtype: :class:`_ACLEntity` 

292 :returns: An Entity corresponding to this user. 

293 """ 

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

295 

296 def group(self, identifier): 

297 """Factory method for a group Entity. 

298 

299 :type identifier: str 

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

301 

302 :rtype: :class:`_ACLEntity` 

303 :returns: An Entity corresponding to this group. 

304 """ 

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

306 

307 def domain(self, domain): 

308 """Factory method for a domain Entity. 

309 

310 :type domain: str 

311 :param domain: The domain for this entity. 

312 

313 :rtype: :class:`_ACLEntity` 

314 :returns: An entity corresponding to this domain. 

315 """ 

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

317 

318 def all(self): 

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

320 

321 :rtype: :class:`_ACLEntity` 

322 :returns: An entity representing all users. 

323 """ 

324 return self.entity("allUsers") 

325 

326 def all_authenticated(self): 

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

328 

329 :rtype: :class:`_ACLEntity` 

330 :returns: An entity representing all authenticated users. 

331 """ 

332 return self.entity("allAuthenticatedUsers") 

333 

334 def get_entities(self): 

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

336 

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

338 :returns: A list of all Entity objects. 

339 """ 

340 self._ensure_loaded() 

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

342 

343 @property 

344 def client(self): 

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

346 raise NotImplementedError 

347 

348 def _require_client(self, client): 

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

350 

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

352 ``NoneType`` 

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

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

355 

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

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

358 """ 

359 if client is None: 

360 client = self.client 

361 return client 

362 

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

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

365 

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

367 

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

369 ``NoneType`` 

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

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

372 :type timeout: float or tuple 

373 :param timeout: 

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

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

376 

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

378 :param retry: 

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

380 """ 

381 path = self.reload_path 

382 client = self._require_client(client) 

383 query_params = {} 

384 

385 if self.user_project is not None: 

386 query_params["userProject"] = self.user_project 

387 

388 self.entities.clear() 

389 

390 found = client._get_resource( 

391 path, 

392 query_params=query_params, 

393 timeout=timeout, 

394 retry=retry, 

395 ) 

396 self.loaded = True 

397 

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

399 self.add_entity(self.entity_from_dict(entry)) 

400 

401 def _save( 

402 self, 

403 acl, 

404 predefined, 

405 client, 

406 if_generation_match=None, 

407 if_generation_not_match=None, 

408 if_metageneration_match=None, 

409 if_metageneration_not_match=None, 

410 timeout=_DEFAULT_TIMEOUT, 

411 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

412 ): 

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

414 

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

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

417 current entries. 

418 

419 :type predefined: str 

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

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

422 

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

424 ``NoneType`` 

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

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

427 

428 :type if_generation_match: long 

429 :param if_generation_match: 

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

431 

432 :type if_generation_not_match: long 

433 :param if_generation_not_match: 

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

435 

436 :type if_metageneration_match: long 

437 :param if_metageneration_match: 

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

439 

440 :type if_metageneration_not_match: long 

441 :param if_metageneration_not_match: 

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

443 

444 :type timeout: float or tuple 

445 :param timeout: 

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

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

448 

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

450 :param retry: 

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

452 """ 

453 client = self._require_client(client) 

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

455 

456 if predefined is not None: 

457 acl = [] 

458 query_params[self._PREDEFINED_QUERY_PARAM] = predefined 

459 

460 if self.user_project is not None: 

461 query_params["userProject"] = self.user_project 

462 

463 _add_generation_match_parameters( 

464 query_params, 

465 if_generation_match=if_generation_match, 

466 if_generation_not_match=if_generation_not_match, 

467 if_metageneration_match=if_metageneration_match, 

468 if_metageneration_not_match=if_metageneration_not_match, 

469 ) 

470 

471 path = self.save_path 

472 

473 result = client._patch_resource( 

474 path, 

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

476 query_params=query_params, 

477 timeout=timeout, 

478 retry=retry, 

479 ) 

480 

481 self.entities.clear() 

482 

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

484 self.add_entity(self.entity_from_dict(entry)) 

485 

486 self.loaded = True 

487 

488 def save( 

489 self, 

490 acl=None, 

491 client=None, 

492 if_generation_match=None, 

493 if_generation_not_match=None, 

494 if_metageneration_match=None, 

495 if_metageneration_not_match=None, 

496 timeout=_DEFAULT_TIMEOUT, 

497 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

498 ): 

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

500 

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

502 

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

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

505 current entries. 

506 

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

508 ``NoneType`` 

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

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

511 

512 :type if_generation_match: long 

513 :param if_generation_match: 

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

515 

516 :type if_generation_not_match: long 

517 :param if_generation_not_match: 

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

519 

520 :type if_metageneration_match: long 

521 :param if_metageneration_match: 

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

523 

524 :type if_metageneration_not_match: long 

525 :param if_metageneration_not_match: 

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

527 

528 :type timeout: float or tuple 

529 :param timeout: 

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

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

532 

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

534 :param retry: 

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

536 """ 

537 if acl is None: 

538 acl = self 

539 save_to_backend = acl.loaded 

540 else: 

541 save_to_backend = True 

542 

543 if save_to_backend: 

544 self._save( 

545 acl, 

546 None, 

547 client, 

548 if_generation_match=if_generation_match, 

549 if_generation_not_match=if_generation_not_match, 

550 if_metageneration_match=if_metageneration_match, 

551 if_metageneration_not_match=if_metageneration_not_match, 

552 timeout=timeout, 

553 retry=retry, 

554 ) 

555 

556 def save_predefined( 

557 self, 

558 predefined, 

559 client=None, 

560 if_generation_match=None, 

561 if_generation_not_match=None, 

562 if_metageneration_match=None, 

563 if_metageneration_not_match=None, 

564 timeout=_DEFAULT_TIMEOUT, 

565 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

566 ): 

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

568 

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

570 

571 :type predefined: str 

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

573 of the keys in :attr:`PREDEFINED_JSON_ACLS` 

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

575 aliased to the corresponding JSON name). 

576 If passed, `acl` must be None. 

577 

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

579 ``NoneType`` 

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

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

582 

583 :type if_generation_match: long 

584 :param if_generation_match: 

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

586 

587 :type if_generation_not_match: long 

588 :param if_generation_not_match: 

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

590 

591 :type if_metageneration_match: long 

592 :param if_metageneration_match: 

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

594 

595 :type if_metageneration_not_match: long 

596 :param if_metageneration_not_match: 

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

598 

599 :type timeout: float or tuple 

600 :param timeout: 

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

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

603 

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

605 :param retry: 

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

607 """ 

608 predefined = self.validate_predefined(predefined) 

609 self._save( 

610 None, 

611 predefined, 

612 client, 

613 if_generation_match=if_generation_match, 

614 if_generation_not_match=if_generation_not_match, 

615 if_metageneration_match=if_metageneration_match, 

616 if_metageneration_not_match=if_metageneration_not_match, 

617 timeout=timeout, 

618 retry=retry, 

619 ) 

620 

621 def clear( 

622 self, 

623 client=None, 

624 if_generation_match=None, 

625 if_generation_not_match=None, 

626 if_metageneration_match=None, 

627 if_metageneration_not_match=None, 

628 timeout=_DEFAULT_TIMEOUT, 

629 retry=DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED, 

630 ): 

631 """Remove all ACL entries. 

632 

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

634 

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

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

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

638 ACL rules with this method. 

639 

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

641 ``NoneType`` 

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

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

644 

645 :type if_generation_match: long 

646 :param if_generation_match: 

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

648 

649 :type if_generation_not_match: long 

650 :param if_generation_not_match: 

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

652 

653 :type if_metageneration_match: long 

654 :param if_metageneration_match: 

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

656 

657 :type if_metageneration_not_match: long 

658 :param if_metageneration_not_match: 

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

660 

661 :type timeout: float or tuple 

662 :param timeout: 

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

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

665 

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

667 :param retry: 

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

669 """ 

670 self.save( 

671 [], 

672 client=client, 

673 if_generation_match=if_generation_match, 

674 if_generation_not_match=if_generation_not_match, 

675 if_metageneration_match=if_metageneration_match, 

676 if_metageneration_not_match=if_metageneration_not_match, 

677 timeout=timeout, 

678 retry=retry, 

679 ) 

680 

681 

682class BucketACL(ACL): 

683 """An ACL specifically for a bucket. 

684 

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

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

687 """ 

688 

689 def __init__(self, bucket): 

690 super(BucketACL, self).__init__() 

691 self.bucket = bucket 

692 

693 @property 

694 def client(self): 

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

696 return self.bucket.client 

697 

698 @property 

699 def reload_path(self): 

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

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

702 

703 @property 

704 def save_path(self): 

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

706 return self.bucket.path 

707 

708 @property 

709 def user_project(self): 

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

711 return self.bucket.user_project 

712 

713 

714class DefaultObjectACL(BucketACL): 

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

716 

717 _URL_PATH_ELEM = "defaultObjectAcl" 

718 _PREDEFINED_QUERY_PARAM = "predefinedDefaultObjectAcl" 

719 

720 

721class ObjectACL(ACL): 

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

723 

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

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

726 """ 

727 

728 def __init__(self, blob): 

729 super(ObjectACL, self).__init__() 

730 self.blob = blob 

731 

732 @property 

733 def client(self): 

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

735 return self.blob.client 

736 

737 @property 

738 def reload_path(self): 

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

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

741 

742 @property 

743 def save_path(self): 

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

745 return self.blob.path 

746 

747 @property 

748 def user_project(self): 

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

750 return self.blob.user_project