Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_default.py692 linesDownload Raw Back to auth
1# Copyright 2015 Google Inc.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 at6#7#      http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# 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 and13# limitations under the License.14 15"""Application default credentials.16 17Implements application default credentials and project ID detection.18"""19 20import io21import json22import logging23import os24import warnings25 26from google.auth import environment_vars27from google.auth import exceptions28import google.auth.transport._http_client29 30_LOGGER = logging.getLogger(__name__)31 32# Valid types accepted for file-based credentials.33_AUTHORIZED_USER_TYPE = "authorized_user"34_SERVICE_ACCOUNT_TYPE = "service_account"35_EXTERNAL_ACCOUNT_TYPE = "external_account"36_EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = "external_account_authorized_user"37_IMPERSONATED_SERVICE_ACCOUNT_TYPE = "impersonated_service_account"38_GDCH_SERVICE_ACCOUNT_TYPE = "gdch_service_account"39_VALID_TYPES = (40    _AUTHORIZED_USER_TYPE,41    _SERVICE_ACCOUNT_TYPE,42    _EXTERNAL_ACCOUNT_TYPE,43    _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE,44    _IMPERSONATED_SERVICE_ACCOUNT_TYPE,45    _GDCH_SERVICE_ACCOUNT_TYPE,46)47 48# Help message when no credentials can be found.49_CLOUD_SDK_MISSING_CREDENTIALS = """\50Your default credentials were not found. To set up Application Default Credentials, \51see https://cloud.google.com/docs/authentication/external/set-up-adc for more information.\52"""53 54# Warning when using Cloud SDK user credentials55_CLOUD_SDK_CREDENTIALS_WARNING = """\56Your application has authenticated using end user credentials from Google \57Cloud SDK without a quota project. You might receive a "quota exceeded" \58or "API not enabled" error. See the following page for troubleshooting: \59https://cloud.google.com/docs/authentication/adc-troubleshooting/user-creds. \60"""61 62# The subject token type used for AWS external_account credentials.63_AWS_SUBJECT_TOKEN_TYPE = "urn:ietf:params:aws:token-type:aws4_request"64 65 66def _warn_about_problematic_credentials(credentials):67    """Determines if the credentials are problematic.68 69    Credentials from the Cloud SDK that are associated with Cloud SDK's project70    are problematic because they may not have APIs enabled and have limited71    quota. If this is the case, warn about it.72    """73    from google.auth import _cloud_sdk74 75    if credentials.client_id == _cloud_sdk.CLOUD_SDK_CLIENT_ID:76        warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)77 78 79def load_credentials_from_file(80    filename, scopes=None, default_scopes=None, quota_project_id=None, request=None81):82    """Loads Google credentials from a file.83 84    The credentials file must be a service account key, stored authorized85    user credentials, external account credentials, or impersonated service86    account credentials.87 88    Args:89        filename (str): The full path to the credentials file.90        scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If91            specified, the credentials will automatically be scoped if92            necessary93        default_scopes (Optional[Sequence[str]]): Default scopes passed by a94            Google client library. Use 'scopes' for user-defined scopes.95        quota_project_id (Optional[str]):  The project ID used for96            quota and billing.97        request (Optional[google.auth.transport.Request]): An object used to make98            HTTP requests. This is used to determine the associated project ID99            for a workload identity pool resource (external account credentials).100            If not specified, then it will use a101            google.auth.transport.requests.Request client to make requests.102 103    Returns:104        Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded105            credentials and the project ID. Authorized user credentials do not106            have the project ID information. External account credentials project107            IDs may not always be determined.108 109    Raises:110        google.auth.exceptions.DefaultCredentialsError: if the file is in the111            wrong format or is missing.112    """113    if not os.path.exists(filename):114        raise exceptions.DefaultCredentialsError(115            "File {} was not found.".format(filename)116        )117 118    with io.open(filename, "r") as file_obj:119        try:120            info = json.load(file_obj)121        except ValueError as caught_exc:122            new_exc = exceptions.DefaultCredentialsError(123                "File {} is not a valid json file.".format(filename), caught_exc124            )125            raise new_exc from caught_exc126    return _load_credentials_from_info(127        filename, info, scopes, default_scopes, quota_project_id, request128    )129 130 131def load_credentials_from_dict(132    info, scopes=None, default_scopes=None, quota_project_id=None, request=None133):134    """Loads Google credentials from a dict.135 136    The credentials file must be a service account key, stored authorized137    user credentials, external account credentials, or impersonated service138    account credentials.139 140    Args:141        info (Dict[str, Any]): A dict object containing the credentials142        scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If143            specified, the credentials will automatically be scoped if144            necessary145        default_scopes (Optional[Sequence[str]]): Default scopes passed by a146            Google client library. Use 'scopes' for user-defined scopes.147        quota_project_id (Optional[str]):  The project ID used for148            quota and billing.149        request (Optional[google.auth.transport.Request]): An object used to make150            HTTP requests. This is used to determine the associated project ID151            for a workload identity pool resource (external account credentials).152            If not specified, then it will use a153            google.auth.transport.requests.Request client to make requests.154 155    Returns:156        Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded157            credentials and the project ID. Authorized user credentials do not158            have the project ID information. External account credentials project159            IDs may not always be determined.160 161    Raises:162        google.auth.exceptions.DefaultCredentialsError: if the file is in the163            wrong format or is missing.164    """165    if not isinstance(info, dict):166        raise exceptions.DefaultCredentialsError(167            "info object was of type {} but dict type was expected.".format(type(info))168        )169 170    return _load_credentials_from_info(171        "dict object", info, scopes, default_scopes, quota_project_id, request172    )173 174 175def _load_credentials_from_info(176    filename, info, scopes, default_scopes, quota_project_id, request177):178    from google.auth.credentials import CredentialsWithQuotaProject179 180    credential_type = info.get("type")181 182    if credential_type == _AUTHORIZED_USER_TYPE:183        credentials, project_id = _get_authorized_user_credentials(184            filename, info, scopes185        )186 187    elif credential_type == _SERVICE_ACCOUNT_TYPE:188        credentials, project_id = _get_service_account_credentials(189            filename, info, scopes, default_scopes190        )191 192    elif credential_type == _EXTERNAL_ACCOUNT_TYPE:193        credentials, project_id = _get_external_account_credentials(194            info,195            filename,196            scopes=scopes,197            default_scopes=default_scopes,198            request=request,199        )200 201    elif credential_type == _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE:202        credentials, project_id = _get_external_account_authorized_user_credentials(203            filename, info, request204        )205 206    elif credential_type == _IMPERSONATED_SERVICE_ACCOUNT_TYPE:207        credentials, project_id = _get_impersonated_service_account_credentials(208            filename, info, scopes209        )210    elif credential_type == _GDCH_SERVICE_ACCOUNT_TYPE:211        credentials, project_id = _get_gdch_service_account_credentials(filename, info)212    else:213        raise exceptions.DefaultCredentialsError(214            "The file {file} does not have a valid type. "215            "Type is {type}, expected one of {valid_types}.".format(216                file=filename, type=credential_type, valid_types=_VALID_TYPES217            )218        )219    if isinstance(credentials, CredentialsWithQuotaProject):220        credentials = _apply_quota_project_id(credentials, quota_project_id)221    return credentials, project_id222 223 224def _get_gcloud_sdk_credentials(quota_project_id=None):225    """Gets the credentials and project ID from the Cloud SDK."""226    from google.auth import _cloud_sdk227 228    _LOGGER.debug("Checking Cloud SDK credentials as part of auth process...")229 230    # Check if application default credentials exist.231    credentials_filename = _cloud_sdk.get_application_default_credentials_path()232 233    if not os.path.isfile(credentials_filename):234        _LOGGER.debug("Cloud SDK credentials not found on disk; not using them")235        return None, None236 237    credentials, project_id = load_credentials_from_file(238        credentials_filename, quota_project_id=quota_project_id239    )240 241    if not project_id:242        project_id = _cloud_sdk.get_project_id()243 244    return credentials, project_id245 246 247def _get_explicit_environ_credentials(quota_project_id=None):248    """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment249    variable."""250    from google.auth import _cloud_sdk251 252    cloud_sdk_adc_path = _cloud_sdk.get_application_default_credentials_path()253    explicit_file = os.environ.get(environment_vars.CREDENTIALS)254 255    _LOGGER.debug(256        "Checking %s for explicit credentials as part of auth process...", explicit_file257    )258 259    if explicit_file is not None and explicit_file == cloud_sdk_adc_path:260        # Cloud sdk flow calls gcloud to fetch project id, so if the explicit261        # file path is cloud sdk credentials path, then we should fall back262        # to cloud sdk flow, otherwise project id cannot be obtained.263        _LOGGER.debug(264            "Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",265            explicit_file,266        )267        return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)268 269    if explicit_file is not None:270        credentials, project_id = load_credentials_from_file(271            os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id272        )273 274        return credentials, project_id275 276    else:277        return None, None278 279 280def _get_gae_credentials():281    """Gets Google App Engine App Identity credentials and project ID."""282    # If not GAE gen1, prefer the metadata service even if the GAE APIs are283    # available as per https://google.aip.dev/auth/4115.284    if os.environ.get(environment_vars.LEGACY_APPENGINE_RUNTIME) != "python27":285        return None, None286 287    # While this library is normally bundled with app_engine, there are288    # some cases where it's not available, so we tolerate ImportError.289    try:290        _LOGGER.debug("Checking for App Engine runtime as part of auth process...")291        import google.auth.app_engine as app_engine292    except ImportError:293        _LOGGER.warning("Import of App Engine auth library failed.")294        return None, None295 296    try:297        credentials = app_engine.Credentials()298        project_id = app_engine.get_project_id()299        return credentials, project_id300    except EnvironmentError:301        _LOGGER.debug(302            "No App Engine library was found so cannot authentication via App Engine Identity Credentials."303        )304        return None, None305 306 307def _get_gce_credentials(request=None, quota_project_id=None):308    """Gets credentials and project ID from the GCE Metadata Service."""309    # Ping requires a transport, but we want application default credentials310    # to require no arguments. So, we'll use the _http_client transport which311    # uses http.client. This is only acceptable because the metadata server312    # doesn't do SSL and never requires proxies.313 314    # While this library is normally bundled with compute_engine, there are315    # some cases where it's not available, so we tolerate ImportError.316    try:317        from google.auth import compute_engine318        from google.auth.compute_engine import _metadata319    except ImportError:320        _LOGGER.warning("Import of Compute Engine auth library failed.")321        return None, None322 323    if request is None:324        request = google.auth.transport._http_client.Request()325 326    if _metadata.is_on_gce(request=request):327        # Get the project ID.328        try:329            project_id = _metadata.get_project_id(request=request)330        except exceptions.TransportError:331            project_id = None332 333        cred = compute_engine.Credentials()334        cred = _apply_quota_project_id(cred, quota_project_id)335 336        return cred, project_id337    else:338        _LOGGER.warning(339            "Authentication failed using Compute Engine authentication due to unavailable metadata server."340        )341        return None, None342 343 344def _get_external_account_credentials(345    info, filename, scopes=None, default_scopes=None, request=None346):347    """Loads external account Credentials from the parsed external account info.348 349    The credentials information must correspond to a supported external account350    credentials.351 352    Args:353        info (Mapping[str, str]): The external account info in Google format.354        filename (str): The full path to the credentials file.355        scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If356            specified, the credentials will automatically be scoped if357            necessary.358        default_scopes (Optional[Sequence[str]]): Default scopes passed by a359            Google client library. Use 'scopes' for user-defined scopes.360        request (Optional[google.auth.transport.Request]): An object used to make361            HTTP requests. This is used to determine the associated project ID362            for a workload identity pool resource (external account credentials).363            If not specified, then it will use a364            google.auth.transport.requests.Request client to make requests.365 366    Returns:367        Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded368            credentials and the project ID. External account credentials project369            IDs may not always be determined.370 371    Raises:372        google.auth.exceptions.DefaultCredentialsError: if the info dictionary373            is in the wrong format or is missing required information.374    """375    # There are currently 3 types of external_account credentials.376    if info.get("subject_token_type") == _AWS_SUBJECT_TOKEN_TYPE:377        # Check if configuration corresponds to an AWS credentials.378        from google.auth import aws379 380        credentials = aws.Credentials.from_info(381            info, scopes=scopes, default_scopes=default_scopes382        )383    elif (384        info.get("credential_source") is not None385        and info.get("credential_source").get("executable") is not None386    ):387        from google.auth import pluggable388 389        credentials = pluggable.Credentials.from_info(390            info, scopes=scopes, default_scopes=default_scopes391        )392    else:393        try:394            # Check if configuration corresponds to an Identity Pool credentials.395            from google.auth import identity_pool396 397            credentials = identity_pool.Credentials.from_info(398                info, scopes=scopes, default_scopes=default_scopes399            )400        except ValueError:401            # If the configuration is invalid or does not correspond to any402            # supported external_account credentials, raise an error.403            raise exceptions.DefaultCredentialsError(404                "Failed to load external account credentials from {}".format(filename)405            )406    if request is None:407        import google.auth.transport.requests408 409        request = google.auth.transport.requests.Request()410 411    return credentials, credentials.get_project_id(request=request)412 413 414def _get_external_account_authorized_user_credentials(415    filename, info, scopes=None, default_scopes=None, request=None416):417    try:418        from google.auth import external_account_authorized_user419 420        credentials = external_account_authorized_user.Credentials.from_info(info)421    except ValueError:422        raise exceptions.DefaultCredentialsError(423            "Failed to load external account authorized user credentials from {}".format(424                filename425            )426        )427 428    return credentials, None429 430 431def _get_authorized_user_credentials(filename, info, scopes=None):432    from google.oauth2 import credentials433 434    try:435        credentials = credentials.Credentials.from_authorized_user_info(436            info, scopes=scopes437        )438    except ValueError as caught_exc:439        msg = "Failed to load authorized user credentials from {}".format(filename)440        new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)441        raise new_exc from caught_exc442    return credentials, None443 444 445def _get_service_account_credentials(filename, info, scopes=None, default_scopes=None):446    from google.oauth2 import service_account447 448    try:449        credentials = service_account.Credentials.from_service_account_info(450            info, scopes=scopes, default_scopes=default_scopes451        )452    except ValueError as caught_exc:453        msg = "Failed to load service account credentials from {}".format(filename)454        new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)455        raise new_exc from caught_exc456    return credentials, info.get("project_id")457 458 459def _get_impersonated_service_account_credentials(filename, info, scopes):460    from google.auth import impersonated_credentials461 462    try:463        source_credentials_info = info.get("source_credentials")464        source_credentials_type = source_credentials_info.get("type")465        if source_credentials_type == _AUTHORIZED_USER_TYPE:466            source_credentials, _ = _get_authorized_user_credentials(467                filename, source_credentials_info468            )469        elif source_credentials_type == _SERVICE_ACCOUNT_TYPE:470            source_credentials, _ = _get_service_account_credentials(471                filename, source_credentials_info472            )473        else:474            raise exceptions.InvalidType(475                "source credential of type {} is not supported.".format(476                    source_credentials_type477                )478            )479        impersonation_url = info.get("service_account_impersonation_url")480        start_index = impersonation_url.rfind("/")481        end_index = impersonation_url.find(":generateAccessToken")482        if start_index == -1 or end_index == -1 or start_index > end_index:483            raise exceptions.InvalidValue(484                "Cannot extract target principal from {}".format(impersonation_url)485            )486        target_principal = impersonation_url[start_index + 1 : end_index]487        delegates = info.get("delegates")488        quota_project_id = info.get("quota_project_id")489        credentials = impersonated_credentials.Credentials(490            source_credentials,491            target_principal,492            scopes,493            delegates,494            quota_project_id=quota_project_id,495        )496    except ValueError as caught_exc:497        msg = "Failed to load impersonated service account credentials from {}".format(498            filename499        )500        new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)501        raise new_exc from caught_exc502    return credentials, None503 504 505def _get_gdch_service_account_credentials(filename, info):506    from google.oauth2 import gdch_credentials507 508    try:509        credentials = gdch_credentials.ServiceAccountCredentials.from_service_account_info(510            info511        )512    except ValueError as caught_exc:513        msg = "Failed to load GDCH service account credentials from {}".format(filename)514        new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)515        raise new_exc from caught_exc516    return credentials, info.get("project")517 518 519def get_api_key_credentials(key):520    """Return credentials with the given API key."""521    from google.auth import api_key522 523    return api_key.Credentials(key)524 525 526def _apply_quota_project_id(credentials, quota_project_id):527    if quota_project_id:528        credentials = credentials.with_quota_project(quota_project_id)529    else:530        credentials = credentials.with_quota_project_from_environment()531 532    from google.oauth2 import credentials as authorized_user_credentials533 534    if isinstance(credentials, authorized_user_credentials.Credentials) and (535        not credentials.quota_project_id536    ):537        _warn_about_problematic_credentials(credentials)538    return credentials539 540 541def default(scopes=None, request=None, quota_project_id=None, default_scopes=None):542    """Gets the default credentials for the current environment.543 544    `Application Default Credentials`_ provides an easy way to obtain545    credentials to call Google APIs for server-to-server or local applications.546    This function acquires credentials from the environment in the following547    order:548 549    1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set550       to the path of a valid service account JSON private key file, then it is551       loaded and returned. The project ID returned is the project ID defined552       in the service account file if available (some older files do not553       contain project ID information).554 555       If the environment variable is set to the path of a valid external556       account JSON configuration file (workload identity federation), then the557       configuration file is used to determine and retrieve the external558       credentials from the current environment (AWS, Azure, etc).559       These will then be exchanged for Google access tokens via the Google STS560       endpoint.561       The project ID returned in this case is the one corresponding to the562       underlying workload identity pool resource if determinable.563 564       If the environment variable is set to the path of a valid GDCH service565       account JSON file (`Google Distributed Cloud Hosted`_), then a GDCH566       credential will be returned. The project ID returned is the project567       specified in the JSON file.568    2. If the `Google Cloud SDK`_ is installed and has application default569       credentials set they are loaded and returned.570 571       To enable application default credentials with the Cloud SDK run::572 573            gcloud auth application-default login574 575       If the Cloud SDK has an active project, the project ID is returned. The576       active project can be set using::577 578            gcloud config set project579 580    3. If the application is running in the `App Engine standard environment`_581       (first generation) then the credentials and project ID from the582       `App Identity Service`_ are used.583    4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or584       the `App Engine flexible environment`_ or the `App Engine standard585       environment`_ (second generation) then the credentials and project ID586       are obtained from the `Metadata Service`_.587    5. If no credentials are found,588       :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.589 590    .. _Application Default Credentials: https://developers.google.com\591            /identity/protocols/application-default-credentials592    .. _Google Cloud SDK: https://cloud.google.com/sdk593    .. _App Engine standard environment: https://cloud.google.com/appengine594    .. _App Identity Service: https://cloud.google.com/appengine/docs/python\595            /appidentity/596    .. _Compute Engine: https://cloud.google.com/compute597    .. _App Engine flexible environment: https://cloud.google.com\598            /appengine/flexible599    .. _Metadata Service: https://cloud.google.com/compute/docs\600            /storing-retrieving-metadata601    .. _Cloud Run: https://cloud.google.com/run602    .. _Google Distributed Cloud Hosted: https://cloud.google.com/blog/topics\603            /hybrid-cloud/announcing-google-distributed-cloud-edge-and-hosted604 605    Example::606 607        import google.auth608 609        credentials, project_id = google.auth.default()610 611    Args:612        scopes (Sequence[str]): The list of scopes for the credentials. If613            specified, the credentials will automatically be scoped if614            necessary.615        request (Optional[google.auth.transport.Request]): An object used to make616            HTTP requests. This is used to either detect whether the application617            is running on Compute Engine or to determine the associated project618            ID for a workload identity pool resource (external account619            credentials). If not specified, then it will either use the standard620            library http client to make requests for Compute Engine credentials621            or a google.auth.transport.requests.Request client for external622            account credentials.623        quota_project_id (Optional[str]): The project ID used for624            quota and billing.625        default_scopes (Optional[Sequence[str]]): Default scopes passed by a626            Google client library. Use 'scopes' for user-defined scopes.627    Returns:628        Tuple[~google.auth.credentials.Credentials, Optional[str]]:629            the current environment's credentials and project ID. Project ID630            may be None, which indicates that the Project ID could not be631            ascertained from the environment.632 633    Raises:634        ~google.auth.exceptions.DefaultCredentialsError:635            If no credentials were found, or if the credentials found were636            invalid.637    """638    from google.auth.credentials import with_scopes_if_required639    from google.auth.credentials import CredentialsWithQuotaProject640 641    explicit_project_id = os.environ.get(642        environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)643    )644 645    checkers = (646        # Avoid passing scopes here to prevent passing scopes to user credentials.647        # with_scopes_if_required() below will ensure scopes/default scopes are648        # safely set on the returned credentials since requires_scopes will649        # guard against setting scopes on user credentials.650        lambda: _get_explicit_environ_credentials(quota_project_id=quota_project_id),651        lambda: _get_gcloud_sdk_credentials(quota_project_id=quota_project_id),652        _get_gae_credentials,653        lambda: _get_gce_credentials(request, quota_project_id=quota_project_id),654    )655 656    for checker in checkers:657        credentials, project_id = checker()658        if credentials is not None:659            credentials = with_scopes_if_required(660                credentials, scopes, default_scopes=default_scopes661            )662 663            effective_project_id = explicit_project_id or project_id664 665            # For external account credentials, scopes are required to determine666            # the project ID. Try to get the project ID again if not yet667            # determined.668            if not effective_project_id and callable(669                getattr(credentials, "get_project_id", None)670            ):671                if request is None:672                    import google.auth.transport.requests673 674                    request = google.auth.transport.requests.Request()675                effective_project_id = credentials.get_project_id(request=request)676 677            if quota_project_id and isinstance(678                credentials, CredentialsWithQuotaProject679            ):680                credentials = credentials.with_quota_project(quota_project_id)681 682            if not effective_project_id:683                _LOGGER.warning(684                    "No project ID could be determined. Consider running "685                    "`gcloud config set project` or setting the %s "686                    "environment variable",687                    environment_vars.PROJECT,688                )689            return credentials, effective_project_id690 691    raise exceptions.DefaultCredentialsError(_CLOUD_SDK_MISSING_CREDENTIALS)692 
codekingpro/portable-devtools · Team Ai