Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/auth/credentials.py: 43%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

241 statements  

1# Copyright 2016 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 

16"""Interfaces for credentials.""" 

17 

18import abc 

19import logging 

20import os 

21import warnings 

22from enum import Enum 

23from typing import TYPE_CHECKING, Dict, List, Optional 

24from urllib.parse import urlparse 

25 

26from google.auth import ( 

27 _helpers, 

28 _regional_access_boundary_utils, 

29 environment_vars, 

30 exceptions, 

31 metrics, 

32) 

33from google.auth._credentials_base import _BaseCredentials 

34from google.auth._refresh_worker import RefreshThreadManager 

35 

36if TYPE_CHECKING: # pragma: NO COVER 

37 import google.auth.transport 

38 

39DEFAULT_UNIVERSE_DOMAIN = _helpers.DEFAULT_UNIVERSE_DOMAIN 

40 

41# These constants are deprecated and no longer used. 

42# They are kept solely for backward compatibility with older implementations. 

43NO_OP_TRUST_BOUNDARY_LOCATIONS: List[str] = [] 

44NO_OP_TRUST_BOUNDARY_ENCODED_LOCATIONS = "0x0" 

45 

46_LOGGER = logging.getLogger("google.auth._default") 

47 

48 

49class Credentials(_BaseCredentials): 

50 """Base class for all credentials. 

51 

52 All credentials have a :attr:`token` that is used for authentication and 

53 may also optionally set an :attr:`expiry` to indicate when the token will 

54 no longer be valid. 

55 

56 Most credentials will be :attr:`invalid` until :meth:`refresh` is called. 

57 Credentials can do this automatically before the first HTTP request in 

58 :meth:`before_request`. 

59 

60 Although the token and expiration will change as the credentials are 

61 :meth:`refreshed <refresh>` and used, credentials should be considered 

62 immutable. Various credentials will accept configuration such as private 

63 keys, scopes, and other options. These options are not changeable after 

64 construction. Some classes will provide mechanisms to copy the credentials 

65 with modifications such as :meth:`ScopedCredentials.with_scopes`. 

66 """ 

67 

68 def __init__(self): 

69 super(Credentials, self).__init__() 

70 

71 self.expiry = None 

72 """Optional[datetime]: When the token expires and is no longer valid. 

73 If this is None, the token is assumed to never expire.""" 

74 self._quota_project_id = None 

75 """Optional[str]: Project to use for quota and billing purposes.""" 

76 self._trust_boundary = None 

77 """Optional[dict]: Cache of a trust boundary response which has a list 

78 of allowed regions and an encoded string representation of credentials 

79 trust boundary.""" 

80 self._universe_domain = DEFAULT_UNIVERSE_DOMAIN 

81 """Optional[str]: The universe domain value, default is googleapis.com 

82 """ 

83 

84 self._use_non_blocking_refresh = False 

85 self._refresh_worker = RefreshThreadManager() 

86 

87 @property 

88 def expired(self): 

89 """Checks if the credentials are expired. 

90 

91 Note that credentials can be invalid but not expired because 

92 Credentials with :attr:`expiry` set to None is considered to never 

93 expire. 

94 

95 .. deprecated:: v2.24.0 

96 Prefer checking :attr:`token_state` instead. 

97 """ 

98 if not self.expiry: 

99 return False 

100 # Remove some threshold from expiry to err on the side of reporting 

101 # expiration early so that we avoid the 401-refresh-retry loop. 

102 skewed_expiry = self.expiry - _helpers.REFRESH_THRESHOLD 

103 return _helpers.utcnow() >= skewed_expiry 

104 

105 @property 

106 def valid(self): 

107 """Checks the validity of the credentials. 

108 

109 This is True if the credentials have a :attr:`token` and the token 

110 is not :attr:`expired`. 

111 

112 .. deprecated:: v2.24.0 

113 Prefer checking :attr:`token_state` instead. 

114 """ 

115 return self.token is not None and not self.expired 

116 

117 @property 

118 def token_state(self): 

119 """ 

120 See `:obj:`TokenState` 

121 """ 

122 if self.token is None: 

123 return TokenState.INVALID 

124 

125 # Credentials that can't expire are always treated as fresh. 

126 if self.expiry is None: 

127 return TokenState.FRESH 

128 

129 expired = _helpers.utcnow() >= self.expiry 

130 if expired: 

131 return TokenState.INVALID 

132 

133 is_stale = _helpers.utcnow() >= (self.expiry - _helpers.REFRESH_THRESHOLD) 

134 if is_stale: 

135 return TokenState.STALE 

136 

137 return TokenState.FRESH 

138 

139 @property 

140 def quota_project_id(self): 

141 """Project to use for quota and billing purposes.""" 

142 return self._quota_project_id 

143 

144 @property 

145 def universe_domain(self): 

146 """The universe domain value.""" 

147 return self._universe_domain 

148 

149 def get_cred_info(self): 

150 """The credential information JSON. 

151 

152 The credential information will be added to auth related error messages 

153 by client library. 

154 

155 Returns: 

156 Mapping[str, str]: The credential information JSON. 

157 """ 

158 return None 

159 

160 @abc.abstractmethod 

161 def refresh(self, request): 

162 """Refreshes the access token. 

163 

164 Args: 

165 request (google.auth.transport.Request): The object used to make 

166 HTTP requests. 

167 

168 Raises: 

169 google.auth.exceptions.RefreshError: If the credentials could 

170 not be refreshed. 

171 """ 

172 # pylint: disable=missing-raises-doc 

173 # (pylint doesn't recognize that this is abstract) 

174 raise NotImplementedError("Refresh must be implemented") 

175 

176 def _metric_header_for_usage(self): 

177 """The x-goog-api-client header for token usage metric. 

178 

179 This header will be added to the API service requests in before_request 

180 method. For example, "cred-type/sa-jwt" means service account self 

181 signed jwt access token is used in the API service request 

182 authorization header. Children credentials classes need to override 

183 this method to provide the header value, if the token usage metric is 

184 needed. 

185 

186 Returns: 

187 str: The x-goog-api-client header value. 

188 """ 

189 return None 

190 

191 def apply(self, headers, token=None): 

192 """Apply the token to the authentication header. 

193 

194 Args: 

195 headers (Mapping): The HTTP request headers. 

196 token (Optional[str]): If specified, overrides the current access 

197 token. 

198 """ 

199 self._apply(headers, token) 

200 if self.quota_project_id: 

201 headers["x-goog-user-project"] = self.quota_project_id 

202 

203 def _blocking_refresh(self, request): 

204 if not self.valid: 

205 self.refresh(request) 

206 

207 def _non_blocking_refresh(self, request): 

208 use_blocking_refresh_fallback = False 

209 

210 if self.token_state == TokenState.STALE: 

211 use_blocking_refresh_fallback = not self._refresh_worker.start_refresh( 

212 self, request 

213 ) 

214 

215 if self.token_state == TokenState.INVALID or use_blocking_refresh_fallback: 

216 self.refresh(request) 

217 # If the blocking refresh succeeds then we can clear the error info 

218 # on the background refresh worker, and perform refreshes in a 

219 # background thread. 

220 self._refresh_worker.clear_error() 

221 

222 def before_request(self, request, method, url, headers): 

223 """Performs credential-specific before request logic. 

224 

225 Refreshes the credentials if necessary, then calls :meth:`apply` to 

226 apply the token to the authentication header. 

227 

228 Args: 

229 request (google.auth.transport.Request): The object used to make 

230 HTTP requests. 

231 method (str): The request's HTTP method or the RPC method being 

232 invoked. 

233 url (str): The request's URI or the RPC service's URI. 

234 headers (Mapping): The request's headers. 

235 """ 

236 # pylint: disable=unused-argument 

237 # (Subclasses may use these arguments to ascertain information about 

238 # the http request.) 

239 if self._use_non_blocking_refresh: 

240 self._non_blocking_refresh(request) 

241 else: 

242 self._blocking_refresh(request) 

243 

244 self._after_refresh(request, method, url, headers) 

245 

246 metrics.add_metric_header(headers, self._metric_header_for_usage()) 

247 self.apply(headers) 

248 

249 def _after_refresh(self, request, method, url, headers): 

250 """Hook for subclasses to perform actions after refresh but before 

251 applying credentials to headers. 

252 

253 Args: 

254 request (google.auth.transport.Request): The object used to make 

255 HTTP requests. 

256 method (str): The request's HTTP method or the RPC method being 

257 invoked. 

258 url (str): The request's URI or the RPC service's URI. 

259 headers (Mapping): The request's headers. 

260 """ 

261 pass 

262 

263 def with_non_blocking_refresh(self): 

264 self._use_non_blocking_refresh = True 

265 

266 

267class CredentialsWithQuotaProject(Credentials): 

268 """Abstract base for credentials supporting ``with_quota_project`` factory""" 

269 

270 def with_quota_project(self, quota_project_id): 

271 """Returns a copy of these credentials with a modified quota project. 

272 

273 Args: 

274 quota_project_id (str): The project to use for quota and 

275 billing purposes 

276 

277 Returns: 

278 google.auth.credentials.Credentials: A new credentials instance. 

279 """ 

280 raise NotImplementedError("This credential does not support quota project.") 

281 

282 def with_quota_project_from_environment(self): 

283 quota_from_env = os.environ.get(environment_vars.GOOGLE_CLOUD_QUOTA_PROJECT) 

284 if quota_from_env: 

285 return self.with_quota_project(quota_from_env) 

286 return self 

287 

288 

289class CredentialsWithTokenUri(Credentials): 

290 """Abstract base for credentials supporting ``with_token_uri`` factory""" 

291 

292 def with_token_uri(self, token_uri): 

293 """Returns a copy of these credentials with a modified token uri. 

294 

295 Args: 

296 token_uri (str): The uri to use for fetching/exchanging tokens 

297 

298 Returns: 

299 google.auth.credentials.Credentials: A new credentials instance. 

300 """ 

301 raise NotImplementedError("This credential does not use token uri.") 

302 

303 

304class CredentialsWithUniverseDomain(Credentials): 

305 """Abstract base for credentials supporting ``with_universe_domain`` factory""" 

306 

307 def with_universe_domain(self, universe_domain): 

308 """Returns a copy of these credentials with a modified universe domain. 

309 

310 Args: 

311 universe_domain (str): The universe domain to use 

312 

313 Returns: 

314 google.auth.credentials.Credentials: A new credentials instance. 

315 """ 

316 raise NotImplementedError( 

317 "This credential does not support with_universe_domain." 

318 ) 

319 

320 

321class CredentialsWithRegionalAccessBoundary(Credentials): 

322 """Abstract base for credentials supporting regional access boundary configuration.""" 

323 

324 def __init__(self): 

325 super().__init__() 

326 self._rab_manager = ( 

327 _regional_access_boundary_utils._RegionalAccessBoundaryManager() 

328 ) 

329 

330 def __setstate__(self, state): 

331 """Pickle helper that restores state, safely reconstructing RAB fields if missing.""" 

332 self.__dict__.update(state) 

333 if "_rab_manager" not in self.__dict__: 

334 from google.auth import _regional_access_boundary_utils 

335 

336 self._rab_manager = ( 

337 _regional_access_boundary_utils._RegionalAccessBoundaryManager() 

338 ) 

339 if "_use_non_blocking_refresh" not in self.__dict__: 

340 self._use_non_blocking_refresh = False 

341 if "_refresh_worker" not in self.__dict__: 

342 from google.auth._refresh_worker import RefreshThreadManager 

343 

344 self._refresh_worker = RefreshThreadManager() 

345 

346 @property 

347 def regional_access_boundary(self): 

348 """Optional[str]: The encoded Regional Access Boundary locations.""" 

349 return self._rab_manager._data.encoded_locations 

350 

351 @property 

352 def regional_access_boundary_expiry(self): 

353 """Optional[datetime.datetime]: The expiration time of the Regional Access Boundary.""" 

354 return self._rab_manager._data.expiry 

355 

356 @abc.abstractmethod 

357 def _perform_refresh_token(self, request): 

358 """Refreshes the access token. 

359 

360 Args: 

361 request (google.auth.transport.Request): The object used to make 

362 HTTP requests. 

363 

364 Raises: 

365 google.auth.exceptions.RefreshError: If the credentials could 

366 not be refreshed. 

367 """ 

368 raise NotImplementedError("_perform_refresh_token must be implemented") 

369 

370 def with_trust_boundary(self, trust_boundary): 

371 """Returns a copy of these credentials. 

372 

373 .. deprecated:: 

374 Manual Regional Access Boundary overrides are not supported. 

375 This method is maintained for backwards compatibility and 

376 returns a copy of the credentials without modifying the 

377 Regional Access Boundary state. 

378 

379 Args: 

380 trust_boundary (Mapping[str, str]): Ignored. 

381 

382 Returns: 

383 google.auth.credentials.Credentials: A new credentials instance. 

384 """ 

385 import warnings 

386 

387 warnings.warn( 

388 "with_trust_boundary is deprecated and has no effect.", 

389 DeprecationWarning, 

390 stacklevel=2, 

391 ) 

392 make_copy = getattr(self, "_make_copy", None) 

393 if make_copy: 

394 return make_copy() 

395 else: 

396 raise NotImplementedError( 

397 "This credential does not support trust boundaries." 

398 ) 

399 

400 def _copy_regional_access_boundary_manager(self, target): 

401 """Copies the regional access boundary manager state to another instance.""" 

402 target._rab_manager._data = self._rab_manager._data 

403 target._rab_manager._use_blocking_regional_access_boundary_lookup = ( 

404 self._rab_manager._use_blocking_regional_access_boundary_lookup 

405 ) 

406 

407 def _set_regional_access_boundary(self, initial_boundary): 

408 """Applies the regional_access_boundary provided via the initial_boundary on these 

409 credentials. This is intended for internal use only as an invalid 

410 initial_boundary would produce unexpected results until automatic recovery 

411 is supported. Currently this is used by the gcloud CLI and therefore changes to the 

412 contract MUST be backwards compatible (e.g. the method signature must be 

413 unchanged and the credentials with the RAB set must be returned). 

414 

415 

416 Returns: 

417 google.auth.credentials.Credentials: The credentials instance. 

418 """ 

419 self._rab_manager.set_initial_regional_access_boundary( 

420 encoded_locations=initial_boundary.get("encodedLocations", None), 

421 expiry=initial_boundary.get("expiry", None), 

422 ) 

423 return self 

424 

425 def _set_blocking_regional_access_boundary_lookup(self): 

426 """Enables the blocking lookup mode on these credentials. 

427 This is intended for internal use only as blocking lookup requires additional 

428 care and consideration. Currently this is used by the gcloud CLI and 

429 therefore changes to the contract MUST be backwards compatible (e.g. the 

430 method signature must be unchanged and the credentials with the 

431 blocking lookup flag set to true must be returned). 

432 

433 Returns: 

434 google.auth.credentials.Credentials: The credentials instance. 

435 """ 

436 self._rab_manager.enable_blocking_lookup() 

437 return self 

438 

439 def _is_regional_endpoint(self, url): 

440 """Checks if the request URL is for a regional endpoint. 

441 

442 Args: 

443 url (str): The URL of the request. 

444 

445 Returns: 

446 bool: True if the URL is a regional endpoint, False otherwise. 

447 """ 

448 try: 

449 # Do not perform a lookup if the request is for a regional endpoint. 

450 hostname = urlparse(url).hostname 

451 if hostname and hostname.endswith( 

452 ( 

453 ".rep.googleapis.com", 

454 ".rep.sandbox.googleapis.com", 

455 ".rep.mtls.googleapis.com", 

456 ".rep.mtls.sandbox.googleapis.com", 

457 ) 

458 ): 

459 return True 

460 except (ValueError, TypeError, AttributeError): 

461 # If the URL is malformed, proceed with the default lookup behavior. 

462 pass 

463 

464 return False 

465 

466 def _maybe_start_regional_access_boundary_refresh(self, request, url): 

467 """ 

468 Starts a background thread to refresh the Regional Access Boundary if needed. 

469 

470 This method checks if a refresh is necessary and if one is not already 

471 in progress or in a cooldown period. If so, it starts a background 

472 thread to perform the lookup. 

473 

474 Args: 

475 request (google.auth.transport.Request): The object used to make 

476 HTTP requests. 

477 url (str): The URL of the request. 

478 """ 

479 # Do not perform a lookup if the request is for a regional endpoint. 

480 if self._is_regional_endpoint(url): 

481 return 

482 

483 # A refresh is only needed if the feature is enabled. 

484 if not self._is_regional_access_boundary_lookup_required(): 

485 return 

486 

487 # Trigger background or blocking refresh if needed 

488 self._rab_manager.maybe_start_refresh(self, request) 

489 

490 def _is_regional_access_boundary_lookup_required(self): 

491 """Checks if a Regional Access Boundary lookup is required. 

492 

493 A lookup is required if the universe domain is supported. 

494 

495 Returns: 

496 bool: True if a Regional Access Boundary lookup is required, False otherwise. 

497 """ 

498 # Skip for non-default universe domains. 

499 if self.universe_domain != DEFAULT_UNIVERSE_DOMAIN: 

500 return False 

501 

502 return True 

503 

504 def apply(self, headers, token=None): 

505 """Apply the token to the authentication header.""" 

506 super().apply(headers, token) 

507 self._rab_manager.apply_headers(headers) 

508 

509 def _after_refresh(self, request, method, url, headers): 

510 """Triggers the Regional Access Boundary lookup if necessary.""" 

511 self._maybe_start_regional_access_boundary_refresh(request, url) 

512 

513 def refresh(self, request): 

514 """Refreshes the access token. 

515 

516 This method calls the subclass's token refresh logic. The Regional 

517 Access Boundary is refreshed separately in a non-blocking way. 

518 """ 

519 self._perform_refresh_token(request) 

520 

521 def _lookup_regional_access_boundary( 

522 self, 

523 request: "google.auth.transport.Request", # noqa: F821 

524 fail_fast: bool = False, 

525 ) -> "Optional[Dict[str, str]]": 

526 """Calls the Regional Access Boundary lookup API to retrieve the Regional Access Boundary information. 

527 

528 Args: 

529 request (google.auth.transport.Request): The object used to make 

530 HTTP requests. 

531 fail_fast (bool): Whether the lookup should fail fast (short timeout, no retries). 

532 

533 Returns: 

534 Optional[Dict[str, str]]: The Regional Access Boundary information returned by the lookup API, or None if the lookup failed. 

535 """ 

536 from google.oauth2 import _client 

537 

538 url = self._build_regional_access_boundary_lookup_url(request=request) 

539 if not url: 

540 _LOGGER.debug("Failed to build Regional Access Boundary lookup URL.") 

541 return None 

542 

543 headers: Dict[str, str] = {} 

544 self._apply(headers) 

545 return _client._lookup_regional_access_boundary( 

546 request, url, headers=headers, fail_fast=fail_fast 

547 ) 

548 

549 @abc.abstractmethod 

550 def _build_regional_access_boundary_lookup_url( 

551 self, 

552 request: "Optional[google.auth.transport.Request]" = None, # noqa: F821 

553 ): 

554 """ 

555 Builds and returns the URL for the Regional Access Boundary lookup API. 

556 

557 This method should be implemented by subclasses to provide the 

558 specific URL based on the credential type and its properties. 

559 

560 Args: 

561 request (Optional[google.auth.transport.Request]): The object used 

562 to make HTTP requests. In some subclasses, this may be used to 

563 make an initial network call to resolve required metadata for the 

564 URL. 

565 

566 Returns: 

567 str: The URL for the Regional Access Boundary lookup endpoint, or None 

568 if lookup should be skipped (e.g., for non-applicable universe domains). 

569 """ 

570 raise NotImplementedError( 

571 "_build_regional_access_boundary_lookup_url must be implemented" 

572 ) 

573 

574 

575class AnonymousCredentials(Credentials): 

576 """Credentials that do not provide any authentication information. 

577 

578 These are useful in the case of services that support anonymous access or 

579 local service emulators that do not use credentials. 

580 """ 

581 

582 @property 

583 def expired(self): 

584 """Returns `False`, anonymous credentials never expire.""" 

585 return False 

586 

587 @property 

588 def valid(self): 

589 """Returns `True`, anonymous credentials are always valid.""" 

590 return True 

591 

592 def refresh(self, request): 

593 """Raises :class:``InvalidOperation``, anonymous credentials cannot be 

594 refreshed.""" 

595 raise exceptions.InvalidOperation("Anonymous credentials cannot be refreshed.") 

596 

597 def apply(self, headers, token=None): 

598 """Anonymous credentials do nothing to the request. 

599 

600 The optional ``token`` argument is not supported. 

601 

602 Raises: 

603 google.auth.exceptions.InvalidValue: If a token was specified. 

604 """ 

605 if token is not None: 

606 raise exceptions.InvalidValue("Anonymous credentials don't support tokens.") 

607 

608 def before_request(self, request, method, url, headers): 

609 """Anonymous credentials do nothing to the request.""" 

610 

611 

612class ReadOnlyScoped(metaclass=abc.ABCMeta): 

613 """Interface for credentials whose scopes can be queried. 

614 

615 OAuth 2.0-based credentials allow limiting access using scopes as described 

616 in `RFC6749 Section 3.3`_. 

617 If a credential class implements this interface then the credentials either 

618 use scopes in their implementation. 

619 

620 Some credentials require scopes in order to obtain a token. You can check 

621 if scoping is necessary with :attr:`requires_scopes`:: 

622 

623 if credentials.requires_scopes: 

624 # Scoping is required. 

625 credentials = credentials.with_scopes(scopes=['one', 'two']) 

626 

627 Credentials that require scopes must either be constructed with scopes:: 

628 

629 credentials = SomeScopedCredentials(scopes=['one', 'two']) 

630 

631 Or must copy an existing instance using :meth:`with_scopes`:: 

632 

633 scoped_credentials = credentials.with_scopes(scopes=['one', 'two']) 

634 

635 Some credentials have scopes but do not allow or require scopes to be set, 

636 these credentials can be used as-is. 

637 

638 .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3 

639 """ 

640 

641 def __init__(self): 

642 super(ReadOnlyScoped, self).__init__() 

643 self._scopes = None 

644 self._default_scopes = None 

645 

646 @property 

647 def scopes(self): 

648 """Sequence[str]: the credentials' current set of scopes.""" 

649 return self._scopes 

650 

651 @property 

652 def default_scopes(self): 

653 """Sequence[str]: the credentials' current set of default scopes.""" 

654 return self._default_scopes 

655 

656 @abc.abstractproperty 

657 def requires_scopes(self): 

658 """True if these credentials require scopes to obtain an access token.""" 

659 return False 

660 

661 def has_scopes(self, scopes): 

662 """Checks if the credentials have the given scopes. 

663 

664 .. warning: This method is not guaranteed to be accurate if the 

665 credentials are :attr:`~Credentials.invalid`. 

666 

667 Args: 

668 scopes (Sequence[str]): The list of scopes to check. 

669 

670 Returns: 

671 bool: True if the credentials have the given scopes. 

672 """ 

673 credential_scopes = ( 

674 self._scopes if self._scopes is not None else self._default_scopes 

675 ) 

676 return set(scopes).issubset(set(credential_scopes or [])) 

677 

678 

679class Scoped(ReadOnlyScoped): 

680 """Interface for credentials whose scopes can be replaced while copying. 

681 

682 OAuth 2.0-based credentials allow limiting access using scopes as described 

683 in `RFC6749 Section 3.3`_. 

684 If a credential class implements this interface then the credentials either 

685 use scopes in their implementation. 

686 

687 Some credentials require scopes in order to obtain a token. You can check 

688 if scoping is necessary with :attr:`requires_scopes`:: 

689 

690 if credentials.requires_scopes: 

691 # Scoping is required. 

692 credentials = credentials.create_scoped(['one', 'two']) 

693 

694 Credentials that require scopes must either be constructed with scopes:: 

695 

696 credentials = SomeScopedCredentials(scopes=['one', 'two']) 

697 

698 Or must copy an existing instance using :meth:`with_scopes`:: 

699 

700 scoped_credentials = credentials.with_scopes(scopes=['one', 'two']) 

701 

702 Some credentials have scopes but do not allow or require scopes to be set, 

703 these credentials can be used as-is. 

704 

705 .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3 

706 """ 

707 

708 @abc.abstractmethod 

709 def with_scopes(self, scopes, default_scopes=None): 

710 """Create a copy of these credentials with the specified scopes. 

711 

712 Args: 

713 scopes (Sequence[str]): The list of scopes to attach to the 

714 current credentials. 

715 

716 Raises: 

717 NotImplementedError: If the credentials' scopes can not be changed. 

718 This can be avoided by checking :attr:`requires_scopes` before 

719 calling this method. 

720 """ 

721 raise NotImplementedError("This class does not require scoping.") 

722 

723 

724def with_scopes_if_required(credentials, scopes, default_scopes=None): 

725 """Creates a copy of the credentials with scopes if scoping is required. 

726 

727 This helper function is useful when you do not know (or care to know) the 

728 specific type of credentials you are using (such as when you use 

729 :func:`google.auth.default`). This function will call 

730 :meth:`Scoped.with_scopes` if the credentials are scoped credentials and if 

731 the credentials require scoping. Otherwise, it will return the credentials 

732 as-is. 

733 

734 Args: 

735 credentials (google.auth.credentials.Credentials): The credentials to 

736 scope if necessary. 

737 scopes (Sequence[str]): The list of scopes to use. 

738 default_scopes (Sequence[str]): Default scopes passed by a 

739 Google client library. Use 'scopes' for user-defined scopes. 

740 

741 Returns: 

742 google.auth.credentials.Credentials: Either a new set of scoped 

743 credentials, or the passed in credentials instance if no scoping 

744 was required. 

745 """ 

746 if isinstance(credentials, Scoped) and credentials.requires_scopes: 

747 return credentials.with_scopes(scopes, default_scopes=default_scopes) 

748 else: 

749 return credentials 

750 

751 

752class Signing(metaclass=abc.ABCMeta): 

753 """Interface for credentials that can cryptographically sign messages.""" 

754 

755 @abc.abstractmethod 

756 def sign_bytes(self, message): 

757 """Signs the given message. 

758 

759 Args: 

760 message (bytes): The message to sign. 

761 

762 Returns: 

763 bytes: The message's cryptographic signature. 

764 """ 

765 # pylint: disable=missing-raises-doc,redundant-returns-doc 

766 # (pylint doesn't recognize that this is abstract) 

767 raise NotImplementedError("Sign bytes must be implemented.") 

768 

769 @abc.abstractproperty 

770 def signer_email(self): 

771 """Optional[str]: An email address that identifies the signer.""" 

772 # pylint: disable=missing-raises-doc 

773 # (pylint doesn't recognize that this is abstract) 

774 raise NotImplementedError("Signer email must be implemented.") 

775 

776 @abc.abstractproperty 

777 def signer(self): 

778 """google.auth.crypt.Signer: The signer used to sign bytes.""" 

779 # pylint: disable=missing-raises-doc 

780 # (pylint doesn't recognize that this is abstract) 

781 raise NotImplementedError("Signer must be implemented.") 

782 

783 

784class TokenState(Enum): 

785 """ 

786 Tracks the state of a token. 

787 FRESH: The token is valid. It is not expired or close to expired, or the token has no expiry. 

788 STALE: The token is close to expired, and should be refreshed. The token can be used normally. 

789 INVALID: The token is expired or invalid. The token cannot be used for a normal operation. 

790 """ 

791 

792 FRESH = 1 

793 STALE = 2 

794 INVALID = 3 

795 

796 

797class CredentialsWithTrustBoundary(CredentialsWithRegionalAccessBoundary): 

798 """Abstract base for credentials supporting legacy trust boundary configuration. 

799 

800 .. deprecated:: 

801 Use :class:`~google.auth.credentials.CredentialsWithRegionalAccessBoundary` instead. 

802 """ 

803 

804 def __init__(self): 

805 super().__init__() 

806 warnings.warn( 

807 "CredentialsWithTrustBoundary is deprecated. Use CredentialsWithRegionalAccessBoundary.", 

808 DeprecationWarning, 

809 stacklevel=2, 

810 ) 

811 

812 @abc.abstractmethod 

813 def _build_trust_boundary_lookup_url(self): 

814 """Deprecated: Implement _build_regional_access_boundary_lookup_url instead.""" 

815 raise NotImplementedError() 

816 

817 def _build_regional_access_boundary_lookup_url(self, request=None): 

818 warnings.warn( 

819 "CredentialsWithTrustBoundary is deprecated. Use CredentialsWithRegionalAccessBoundary.", 

820 DeprecationWarning, 

821 stacklevel=2, 

822 ) 

823 return self._build_trust_boundary_lookup_url()