Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
credentials.py2298 linesDownload Raw Back to botocore
1# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/2# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.3#4# Licensed under the Apache License, Version 2.0 (the "License"). You5# may not use this file except in compliance with the License. A copy of6# the License is located at7#8# http://aws.amazon.com/apache2.0/9#10# or in the "license" file accompanying this file. This file is11# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF12# ANY KIND, either express or implied. See the License for the specific13# language governing permissions and limitations under the License.14import datetime15import getpass16import json17import logging18import os19import subprocess20import threading21import time22from collections import namedtuple23from copy import deepcopy24from hashlib import sha125 26from dateutil.parser import parse27from dateutil.tz import tzlocal, tzutc28 29import botocore.compat30import botocore.configloader31from botocore import UNSIGNED32from botocore.compat import compat_shell_split, total_seconds33from botocore.config import Config34from botocore.exceptions import (35    ConfigNotFound,36    CredentialRetrievalError,37    InfiniteLoopConfigError,38    InvalidConfigError,39    MetadataRetrievalError,40    PartialCredentialsError,41    RefreshWithMFAUnsupportedError,42    UnauthorizedSSOTokenError,43    UnknownCredentialError,44)45from botocore.tokens import SSOTokenProvider46from botocore.utils import (47    ContainerMetadataFetcher,48    FileWebIdentityTokenLoader,49    InstanceMetadataFetcher,50    JSONFileCache,51    SSOTokenLoader,52    parse_key_val_file,53    resolve_imds_endpoint_mode,54)55 56logger = logging.getLogger(__name__)57ReadOnlyCredentials = namedtuple(58    'ReadOnlyCredentials', ['access_key', 'secret_key', 'token']59)60 61_DEFAULT_MANDATORY_REFRESH_TIMEOUT = 10 * 60  # 10 min62_DEFAULT_ADVISORY_REFRESH_TIMEOUT = 15 * 60  # 15 min63 64 65def create_credential_resolver(session, cache=None, region_name=None):66    """Create a default credential resolver.67 68    This creates a pre-configured credential resolver69    that includes the default lookup chain for70    credentials.71 72    """73    profile_name = session.get_config_variable('profile') or 'default'74    metadata_timeout = session.get_config_variable('metadata_service_timeout')75    num_attempts = session.get_config_variable('metadata_service_num_attempts')76    disable_env_vars = session.instance_variables().get('profile') is not None77 78    imds_config = {79        'ec2_metadata_service_endpoint': session.get_config_variable(80            'ec2_metadata_service_endpoint'81        ),82        'ec2_metadata_service_endpoint_mode': resolve_imds_endpoint_mode(83            session84        ),85        'ec2_credential_refresh_window': _DEFAULT_ADVISORY_REFRESH_TIMEOUT,86        'ec2_metadata_v1_disabled': session.get_config_variable(87            'ec2_metadata_v1_disabled'88        ),89    }90 91    if cache is None:92        cache = {}93 94    env_provider = EnvProvider()95    container_provider = ContainerProvider()96    instance_metadata_provider = InstanceMetadataProvider(97        iam_role_fetcher=InstanceMetadataFetcher(98            timeout=metadata_timeout,99            num_attempts=num_attempts,100            user_agent=session.user_agent(),101            config=imds_config,102        )103    )104 105    profile_provider_builder = ProfileProviderBuilder(106        session, cache=cache, region_name=region_name107    )108    assume_role_provider = AssumeRoleProvider(109        load_config=lambda: session.full_config,110        client_creator=_get_client_creator(session, region_name),111        cache=cache,112        profile_name=profile_name,113        credential_sourcer=CanonicalNameCredentialSourcer(114            [env_provider, container_provider, instance_metadata_provider]115        ),116        profile_provider_builder=profile_provider_builder,117    )118 119    pre_profile = [120        env_provider,121        assume_role_provider,122    ]123    profile_providers = profile_provider_builder.providers(124        profile_name=profile_name,125        disable_env_vars=disable_env_vars,126    )127    post_profile = [128        OriginalEC2Provider(),129        BotoProvider(),130        container_provider,131        instance_metadata_provider,132    ]133    providers = pre_profile + profile_providers + post_profile134 135    if disable_env_vars:136        # An explicitly provided profile will negate an EnvProvider.137        # We will defer to providers that understand the "profile"138        # concept to retrieve credentials.139        # The one edge case if is all three values are provided via140        # env vars:141        # export AWS_ACCESS_KEY_ID=foo142        # export AWS_SECRET_ACCESS_KEY=bar143        # export AWS_PROFILE=baz144        # Then, just like our client() calls, the explicit credentials145        # will take precedence.146        #147        # This precedence is enforced by leaving the EnvProvider in the chain.148        # This means that the only way a "profile" would win is if the149        # EnvProvider does not return credentials, which is what we want150        # in this scenario.151        providers.remove(env_provider)152        logger.debug(153            'Skipping environment variable credential check'154            ' because profile name was explicitly set.'155        )156 157    resolver = CredentialResolver(providers=providers)158    return resolver159 160 161class ProfileProviderBuilder:162    """This class handles the creation of profile based providers.163 164    NOTE: This class is only intended for internal use.165 166    This class handles the creation and ordering of the various credential167    providers that primarly source their configuration from the shared config.168    This is needed to enable sharing between the default credential chain and169    the source profile chain created by the assume role provider.170    """171 172    def __init__(173        self, session, cache=None, region_name=None, sso_token_cache=None174    ):175        self._session = session176        self._cache = cache177        self._region_name = region_name178        self._sso_token_cache = sso_token_cache179 180    def providers(self, profile_name, disable_env_vars=False):181        return [182            self._create_web_identity_provider(183                profile_name,184                disable_env_vars,185            ),186            self._create_sso_provider(profile_name),187            self._create_shared_credential_provider(profile_name),188            self._create_process_provider(profile_name),189            self._create_config_provider(profile_name),190        ]191 192    def _create_process_provider(self, profile_name):193        return ProcessProvider(194            profile_name=profile_name,195            load_config=lambda: self._session.full_config,196        )197 198    def _create_shared_credential_provider(self, profile_name):199        credential_file = self._session.get_config_variable('credentials_file')200        return SharedCredentialProvider(201            profile_name=profile_name,202            creds_filename=credential_file,203        )204 205    def _create_config_provider(self, profile_name):206        config_file = self._session.get_config_variable('config_file')207        return ConfigProvider(208            profile_name=profile_name,209            config_filename=config_file,210        )211 212    def _create_web_identity_provider(self, profile_name, disable_env_vars):213        return AssumeRoleWithWebIdentityProvider(214            load_config=lambda: self._session.full_config,215            client_creator=_get_client_creator(216                self._session, self._region_name217            ),218            cache=self._cache,219            profile_name=profile_name,220            disable_env_vars=disable_env_vars,221        )222 223    def _create_sso_provider(self, profile_name):224        return SSOProvider(225            load_config=lambda: self._session.full_config,226            client_creator=self._session.create_client,227            profile_name=profile_name,228            cache=self._cache,229            token_cache=self._sso_token_cache,230            token_provider=SSOTokenProvider(231                self._session,232                cache=self._sso_token_cache,233                profile_name=profile_name,234            ),235        )236 237 238def get_credentials(session):239    resolver = create_credential_resolver(session)240    return resolver.load_credentials()241 242 243def _local_now():244    return datetime.datetime.now(tzlocal())245 246 247def _parse_if_needed(value):248    if isinstance(value, datetime.datetime):249        return value250    return parse(value)251 252 253def _serialize_if_needed(value, iso=False):254    if isinstance(value, datetime.datetime):255        if iso:256            return value.isoformat()257        return value.strftime('%Y-%m-%dT%H:%M:%S%Z')258    return value259 260 261def _get_client_creator(session, region_name):262    def client_creator(service_name, **kwargs):263        create_client_kwargs = {'region_name': region_name}264        create_client_kwargs.update(**kwargs)265        return session.create_client(service_name, **create_client_kwargs)266 267    return client_creator268 269 270def create_assume_role_refresher(client, params):271    def refresh():272        response = client.assume_role(**params)273        credentials = response['Credentials']274        # We need to normalize the credential names to275        # the values expected by the refresh creds.276        return {277            'access_key': credentials['AccessKeyId'],278            'secret_key': credentials['SecretAccessKey'],279            'token': credentials['SessionToken'],280            'expiry_time': _serialize_if_needed(credentials['Expiration']),281        }282 283    return refresh284 285 286def create_mfa_serial_refresher(actual_refresh):287    class _Refresher:288        def __init__(self, refresh):289            self._refresh = refresh290            self._has_been_called = False291 292        def __call__(self):293            if self._has_been_called:294                # We can explore an option in the future to support295                # reprompting for MFA, but for now we just error out296                # when the temp creds expire.297                raise RefreshWithMFAUnsupportedError()298            self._has_been_called = True299            return self._refresh()300 301    return _Refresher(actual_refresh)302 303 304class Credentials:305    """306    Holds the credentials needed to authenticate requests.307 308    :param str access_key: The access key part of the credentials.309    :param str secret_key: The secret key part of the credentials.310    :param str token: The security token, valid only for session credentials.311    :param str method: A string which identifies where the credentials312        were found.313    """314 315    def __init__(self, access_key, secret_key, token=None, method=None):316        self.access_key = access_key317        self.secret_key = secret_key318        self.token = token319 320        if method is None:321            method = 'explicit'322        self.method = method323 324        self._normalize()325 326    def _normalize(self):327        # Keys would sometimes (accidentally) contain non-ascii characters.328        # It would cause a confusing UnicodeDecodeError in Python 2.329        # We explicitly convert them into unicode to avoid such error.330        #331        # Eventually the service will decide whether to accept the credential.332        # This also complies with the behavior in Python 3.333        self.access_key = botocore.compat.ensure_unicode(self.access_key)334        self.secret_key = botocore.compat.ensure_unicode(self.secret_key)335 336    def get_frozen_credentials(self):337        return ReadOnlyCredentials(338            self.access_key, self.secret_key, self.token339        )340 341 342class RefreshableCredentials(Credentials):343    """344    Holds the credentials needed to authenticate requests. In addition, it345    knows how to refresh itself.346 347    :param str access_key: The access key part of the credentials.348    :param str secret_key: The secret key part of the credentials.349    :param str token: The security token, valid only for session credentials.350    :param datetime expiry_time: The expiration time of the credentials.351    :param function refresh_using: Callback function to refresh the credentials.352    :param str method: A string which identifies where the credentials353        were found.354    :param function time_fetcher: Callback function to retrieve current time.355    """356 357    # The time at which we'll attempt to refresh, but not358    # block if someone else is refreshing.359    _advisory_refresh_timeout = _DEFAULT_ADVISORY_REFRESH_TIMEOUT360    # The time at which all threads will block waiting for361    # refreshed credentials.362    _mandatory_refresh_timeout = _DEFAULT_MANDATORY_REFRESH_TIMEOUT363 364    def __init__(365        self,366        access_key,367        secret_key,368        token,369        expiry_time,370        refresh_using,371        method,372        time_fetcher=_local_now,373        advisory_timeout=None,374        mandatory_timeout=None,375    ):376        self._refresh_using = refresh_using377        self._access_key = access_key378        self._secret_key = secret_key379        self._token = token380        self._expiry_time = expiry_time381        self._time_fetcher = time_fetcher382        self._refresh_lock = threading.Lock()383        self.method = method384        self._frozen_credentials = ReadOnlyCredentials(385            access_key, secret_key, token386        )387        self._normalize()388        if advisory_timeout is not None:389            self._advisory_refresh_timeout = advisory_timeout390        if mandatory_timeout is not None:391            self._mandatory_refresh_timeout = mandatory_timeout392 393    def _normalize(self):394        self._access_key = botocore.compat.ensure_unicode(self._access_key)395        self._secret_key = botocore.compat.ensure_unicode(self._secret_key)396 397    @classmethod398    def create_from_metadata(399        cls,400        metadata,401        refresh_using,402        method,403        advisory_timeout=None,404        mandatory_timeout=None,405    ):406        kwargs = {}407        if advisory_timeout is not None:408            kwargs['advisory_timeout'] = advisory_timeout409        if mandatory_timeout is not None:410            kwargs['mandatory_timeout'] = mandatory_timeout411 412        instance = cls(413            access_key=metadata['access_key'],414            secret_key=metadata['secret_key'],415            token=metadata['token'],416            expiry_time=cls._expiry_datetime(metadata['expiry_time']),417            method=method,418            refresh_using=refresh_using,419            **kwargs,420        )421        return instance422 423    @property424    def access_key(self):425        """Warning: Using this property can lead to race conditions if you426        access another property subsequently along the refresh boundary.427        Please use get_frozen_credentials instead.428        """429        self._refresh()430        return self._access_key431 432    @access_key.setter433    def access_key(self, value):434        self._access_key = value435 436    @property437    def secret_key(self):438        """Warning: Using this property can lead to race conditions if you439        access another property subsequently along the refresh boundary.440        Please use get_frozen_credentials instead.441        """442        self._refresh()443        return self._secret_key444 445    @secret_key.setter446    def secret_key(self, value):447        self._secret_key = value448 449    @property450    def token(self):451        """Warning: Using this property can lead to race conditions if you452        access another property subsequently along the refresh boundary.453        Please use get_frozen_credentials instead.454        """455        self._refresh()456        return self._token457 458    @token.setter459    def token(self, value):460        self._token = value461 462    def _seconds_remaining(self):463        delta = self._expiry_time - self._time_fetcher()464        return total_seconds(delta)465 466    def refresh_needed(self, refresh_in=None):467        """Check if a refresh is needed.468 469        A refresh is needed if the expiry time associated470        with the temporary credentials is less than the471        provided ``refresh_in``.  If ``time_delta`` is not472        provided, ``self.advisory_refresh_needed`` will be used.473 474        For example, if your temporary credentials expire475        in 10 minutes and the provided ``refresh_in`` is476        ``15 * 60``, then this function will return ``True``.477 478        :type refresh_in: int479        :param refresh_in: The number of seconds before the480            credentials expire in which refresh attempts should481            be made.482 483        :return: True if refresh needed, False otherwise.484 485        """486        if self._expiry_time is None:487            # No expiration, so assume we don't need to refresh.488            return False489 490        if refresh_in is None:491            refresh_in = self._advisory_refresh_timeout492        # The credentials should be refreshed if they're going to expire493        # in less than 5 minutes.494        if self._seconds_remaining() >= refresh_in:495            # There's enough time left. Don't refresh.496            return False497        logger.debug("Credentials need to be refreshed.")498        return True499 500    def _is_expired(self):501        # Checks if the current credentials are expired.502        return self.refresh_needed(refresh_in=0)503 504    def _refresh(self):505        # In the common case where we don't need a refresh, we506        # can immediately exit and not require acquiring the507        # refresh lock.508        if not self.refresh_needed(self._advisory_refresh_timeout):509            return510 511        # acquire() doesn't accept kwargs, but False is indicating512        # that we should not block if we can't acquire the lock.513        # If we aren't able to acquire the lock, we'll trigger514        # the else clause.515        if self._refresh_lock.acquire(False):516            try:517                if not self.refresh_needed(self._advisory_refresh_timeout):518                    return519                is_mandatory_refresh = self.refresh_needed(520                    self._mandatory_refresh_timeout521                )522                self._protected_refresh(is_mandatory=is_mandatory_refresh)523                return524            finally:525                self._refresh_lock.release()526        elif self.refresh_needed(self._mandatory_refresh_timeout):527            # If we're within the mandatory refresh window,528            # we must block until we get refreshed credentials.529            with self._refresh_lock:530                if not self.refresh_needed(self._mandatory_refresh_timeout):531                    return532                self._protected_refresh(is_mandatory=True)533 534    def _protected_refresh(self, is_mandatory):535        # precondition: this method should only be called if you've acquired536        # the self._refresh_lock.537        try:538            metadata = self._refresh_using()539        except Exception:540            period_name = 'mandatory' if is_mandatory else 'advisory'541            logger.warning(542                "Refreshing temporary credentials failed "543                "during %s refresh period.",544                period_name,545                exc_info=True,546            )547            if is_mandatory:548                # If this is a mandatory refresh, then549                # all errors that occur when we attempt to refresh550                # credentials are propagated back to the user.551                raise552            # Otherwise we'll just return.553            # The end result will be that we'll use the current554            # set of temporary credentials we have.555            return556        self._set_from_data(metadata)557        self._frozen_credentials = ReadOnlyCredentials(558            self._access_key, self._secret_key, self._token559        )560        if self._is_expired():561            # We successfully refreshed credentials but for whatever562            # reason, our refreshing function returned credentials563            # that are still expired.  In this scenario, the only564            # thing we can do is let the user know and raise565            # an exception.566            msg = (567                "Credentials were refreshed, but the "568                "refreshed credentials are still expired."569            )570            logger.warning(msg)571            raise RuntimeError(msg)572 573    @staticmethod574    def _expiry_datetime(time_str):575        return parse(time_str)576 577    def _set_from_data(self, data):578        expected_keys = ['access_key', 'secret_key', 'token', 'expiry_time']579        if not data:580            missing_keys = expected_keys581        else:582            missing_keys = [k for k in expected_keys if k not in data]583 584        if missing_keys:585            message = "Credential refresh failed, response did not contain: %s"586            raise CredentialRetrievalError(587                provider=self.method,588                error_msg=message % ', '.join(missing_keys),589            )590 591        self.access_key = data['access_key']592        self.secret_key = data['secret_key']593        self.token = data['token']594        self._expiry_time = parse(data['expiry_time'])595        logger.debug(596            "Retrieved credentials will expire at: %s", self._expiry_time597        )598        self._normalize()599 600    def get_frozen_credentials(self):601        """Return immutable credentials.602 603        The ``access_key``, ``secret_key``, and ``token`` properties604        on this class will always check and refresh credentials if605        needed before returning the particular credentials.606 607        This has an edge case where you can get inconsistent608        credentials.  Imagine this:609 610            # Current creds are "t1"611            tmp.access_key  ---> expired? no, so return t1.access_key612            # ---- time is now expired, creds need refreshing to "t2" ----613            tmp.secret_key  ---> expired? yes, refresh and return t2.secret_key614 615        This means we're using the access key from t1 with the secret key616        from t2.  To fix this issue, you can request a frozen credential object617        which is guaranteed not to change.618 619        The frozen credentials returned from this method should be used620        immediately and then discarded.  The typical usage pattern would621        be::622 623            creds = RefreshableCredentials(...)624            some_code = SomeSignerObject()625            # I'm about to sign the request.626            # The frozen credentials are only used for the627            # duration of generate_presigned_url and will be628            # immediately thrown away.629            request = some_code.sign_some_request(630                with_credentials=creds.get_frozen_credentials())631            print("Signed request:", request)632 633        """634        self._refresh()635        return self._frozen_credentials636 637 638class DeferredRefreshableCredentials(RefreshableCredentials):639    """Refreshable credentials that don't require initial credentials.640 641    refresh_using will be called upon first access.642    """643 644    def __init__(self, refresh_using, method, time_fetcher=_local_now):645        self._refresh_using = refresh_using646        self._access_key = None647        self._secret_key = None648        self._token = None649        self._expiry_time = None650        self._time_fetcher = time_fetcher651        self._refresh_lock = threading.Lock()652        self.method = method653        self._frozen_credentials = None654 655    def refresh_needed(self, refresh_in=None):656        if self._frozen_credentials is None:657            return True658        return super().refresh_needed(refresh_in)659 660 661class CachedCredentialFetcher:662    DEFAULT_EXPIRY_WINDOW_SECONDS = 60 * 15663 664    def __init__(self, cache=None, expiry_window_seconds=None):665        if cache is None:666            cache = {}667        self._cache = cache668        self._cache_key = self._create_cache_key()669        if expiry_window_seconds is None:670            expiry_window_seconds = self.DEFAULT_EXPIRY_WINDOW_SECONDS671        self._expiry_window_seconds = expiry_window_seconds672 673    def _create_cache_key(self):674        raise NotImplementedError('_create_cache_key()')675 676    def _make_file_safe(self, filename):677        # Replace :, path sep, and / to make it the string filename safe.678        filename = filename.replace(':', '_').replace(os.sep, '_')679        return filename.replace('/', '_')680 681    def _get_credentials(self):682        raise NotImplementedError('_get_credentials()')683 684    def fetch_credentials(self):685        return self._get_cached_credentials()686 687    def _get_cached_credentials(self):688        """Get up-to-date credentials.689 690        This will check the cache for up-to-date credentials, calling assume691        role if none are available.692        """693        response = self._load_from_cache()694        if response is None:695            response = self._get_credentials()696            self._write_to_cache(response)697        else:698            logger.debug("Credentials for role retrieved from cache.")699 700        creds = response['Credentials']701        expiration = _serialize_if_needed(creds['Expiration'], iso=True)702        return {703            'access_key': creds['AccessKeyId'],704            'secret_key': creds['SecretAccessKey'],705            'token': creds['SessionToken'],706            'expiry_time': expiration,707        }708 709    def _load_from_cache(self):710        if self._cache_key in self._cache:711            creds = deepcopy(self._cache[self._cache_key])712            if not self._is_expired(creds):713                return creds714            else:715                logger.debug(716                    "Credentials were found in cache, but they are expired."717                )718        return None719 720    def _write_to_cache(self, response):721        self._cache[self._cache_key] = deepcopy(response)722 723    def _is_expired(self, credentials):724        """Check if credentials are expired."""725        end_time = _parse_if_needed(credentials['Credentials']['Expiration'])726        seconds = total_seconds(end_time - _local_now())727        return seconds < self._expiry_window_seconds728 729 730class BaseAssumeRoleCredentialFetcher(CachedCredentialFetcher):731    def __init__(732        self,733        client_creator,734        role_arn,735        extra_args=None,736        cache=None,737        expiry_window_seconds=None,738    ):739        self._client_creator = client_creator740        self._role_arn = role_arn741 742        if extra_args is None:743            self._assume_kwargs = {}744        else:745            self._assume_kwargs = deepcopy(extra_args)746        self._assume_kwargs['RoleArn'] = self._role_arn747 748        self._role_session_name = self._assume_kwargs.get('RoleSessionName')749        self._using_default_session_name = False750        if not self._role_session_name:751            self._generate_assume_role_name()752 753        super().__init__(cache, expiry_window_seconds)754 755    def _generate_assume_role_name(self):756        self._role_session_name = 'botocore-session-%s' % (int(time.time()))757        self._assume_kwargs['RoleSessionName'] = self._role_session_name758        self._using_default_session_name = True759 760    def _create_cache_key(self):761        """Create a predictable cache key for the current configuration.762 763        The cache key is intended to be compatible with file names.764        """765        args = deepcopy(self._assume_kwargs)766 767        # The role session name gets randomly generated, so we don't want it768        # in the hash.769        if self._using_default_session_name:770            del args['RoleSessionName']771 772        if 'Policy' in args:773            # To have a predictable hash, the keys of the policy must be774            # sorted, so we have to load it here to make sure it gets sorted775            # later on.776            args['Policy'] = json.loads(args['Policy'])777 778        args = json.dumps(args, sort_keys=True)779        argument_hash = sha1(args.encode('utf-8')).hexdigest()780        return self._make_file_safe(argument_hash)781 782 783class AssumeRoleCredentialFetcher(BaseAssumeRoleCredentialFetcher):784    def __init__(785        self,786        client_creator,787        source_credentials,788        role_arn,789        extra_args=None,790        mfa_prompter=None,791        cache=None,792        expiry_window_seconds=None,793    ):794        """795        :type client_creator: callable796        :param client_creator: A callable that creates a client taking797            arguments like ``Session.create_client``.798 799        :type source_credentials: Credentials800        :param source_credentials: The credentials to use to create the801            client for the call to AssumeRole.802 803        :type role_arn: str804        :param role_arn: The ARN of the role to be assumed.805 806        :type extra_args: dict807        :param extra_args: Any additional arguments to add to the assume808            role request using the format of the botocore operation.809            Possible keys include, but may not be limited to,810            DurationSeconds, Policy, SerialNumber, ExternalId and811            RoleSessionName.812 813        :type mfa_prompter: callable814        :param mfa_prompter: A callable that returns input provided by the815            user (i.e raw_input, getpass.getpass, etc.).816 817        :type cache: dict818        :param cache: An object that supports ``__getitem__``,819            ``__setitem__``, and ``__contains__``.  An example of this is820            the ``JSONFileCache`` class in aws-cli.821 822        :type expiry_window_seconds: int823        :param expiry_window_seconds: The amount of time, in seconds,824        """825        self._source_credentials = source_credentials826        self._mfa_prompter = mfa_prompter827        if self._mfa_prompter is None:828            self._mfa_prompter = getpass.getpass829 830        super().__init__(831            client_creator,832            role_arn,833            extra_args=extra_args,834            cache=cache,835            expiry_window_seconds=expiry_window_seconds,836        )837 838    def _get_credentials(self):839        """Get credentials by calling assume role."""840        kwargs = self._assume_role_kwargs()841        client = self._create_client()842        return client.assume_role(**kwargs)843 844    def _assume_role_kwargs(self):845        """Get the arguments for assume role based on current configuration."""846        assume_role_kwargs = deepcopy(self._assume_kwargs)847 848        mfa_serial = assume_role_kwargs.get('SerialNumber')849 850        if mfa_serial is not None:851            prompt = 'Enter MFA code for %s: ' % mfa_serial852            token_code = self._mfa_prompter(prompt)853            assume_role_kwargs['TokenCode'] = token_code854 855        duration_seconds = assume_role_kwargs.get('DurationSeconds')856 857        if duration_seconds is not None:858            assume_role_kwargs['DurationSeconds'] = duration_seconds859 860        return assume_role_kwargs861 862    def _create_client(self):863        """Create an STS client using the source credentials."""864        frozen_credentials = self._source_credentials.get_frozen_credentials()865        return self._client_creator(866            'sts',867            aws_access_key_id=frozen_credentials.access_key,868            aws_secret_access_key=frozen_credentials.secret_key,869            aws_session_token=frozen_credentials.token,870        )871 872 873class AssumeRoleWithWebIdentityCredentialFetcher(874    BaseAssumeRoleCredentialFetcher875):876    def __init__(877        self,878        client_creator,879        web_identity_token_loader,880        role_arn,881        extra_args=None,882        cache=None,883        expiry_window_seconds=None,884    ):885        """886        :type client_creator: callable887        :param client_creator: A callable that creates a client taking888            arguments like ``Session.create_client``.889 890        :type web_identity_token_loader: callable891        :param web_identity_token_loader: A callable that takes no arguments892        and returns a web identity token str.893 894        :type role_arn: str895        :param role_arn: The ARN of the role to be assumed.896 897        :type extra_args: dict898        :param extra_args: Any additional arguments to add to the assume899            role request using the format of the botocore operation.900            Possible keys include, but may not be limited to,901            DurationSeconds, Policy, SerialNumber, ExternalId and902            RoleSessionName.903 904        :type cache: dict905        :param cache: An object that supports ``__getitem__``,906            ``__setitem__``, and ``__contains__``.  An example of this is907            the ``JSONFileCache`` class in aws-cli.908 909        :type expiry_window_seconds: int910        :param expiry_window_seconds: The amount of time, in seconds,911        """912        self._web_identity_token_loader = web_identity_token_loader913 914        super().__init__(915            client_creator,916            role_arn,917            extra_args=extra_args,918            cache=cache,919            expiry_window_seconds=expiry_window_seconds,920        )921 922    def _get_credentials(self):923        """Get credentials by calling assume role."""924        kwargs = self._assume_role_kwargs()925        # Assume role with web identity does not require credentials other than926        # the token, explicitly configure the client to not sign requests.927        config = Config(signature_version=UNSIGNED)928        client = self._client_creator('sts', config=config)929        return client.assume_role_with_web_identity(**kwargs)930 931    def _assume_role_kwargs(self):932        """Get the arguments for assume role based on current configuration."""933        assume_role_kwargs = deepcopy(self._assume_kwargs)934        identity_token = self._web_identity_token_loader()935        assume_role_kwargs['WebIdentityToken'] = identity_token936 937        return assume_role_kwargs938 939 940class CredentialProvider:941    # A short name to identify the provider within botocore.942    METHOD = None943 944    # A name to identify the provider for use in cross-sdk features like945    # assume role's `credential_source` configuration option. These names946    # are to be treated in a case-insensitive way. NOTE: any providers not947    # implemented in botocore MUST prefix their canonical names with948    # 'custom' or we DO NOT guarantee that it will work with any features949    # that this provides.950    CANONICAL_NAME = None951 952    def __init__(self, session=None):953        self.session = session954 955    def load(self):956        """957        Loads the credentials from their source & sets them on the object.958 959        Subclasses should implement this method (by reading from disk, the960        environment, the network or wherever), returning ``True`` if they were961        found & loaded.962 963        If not found, this method should return ``False``, indictating that the964        ``CredentialResolver`` should fall back to the next available method.965 966        The default implementation does nothing, assuming the user has set the967        ``access_key/secret_key/token`` themselves.968 969        :returns: Whether credentials were found & set970        :rtype: Credentials971        """972        return True973 974    def _extract_creds_from_mapping(self, mapping, *key_names):975        found = []976        for key_name in key_names:977            try:978                found.append(mapping[key_name])979            except KeyError:980                raise PartialCredentialsError(981                    provider=self.METHOD, cred_var=key_name982                )983        return found984 985 986class ProcessProvider(CredentialProvider):987    METHOD = 'custom-process'988 989    def __init__(self, profile_name, load_config, popen=subprocess.Popen):990        self._profile_name = profile_name991        self._load_config = load_config992        self._loaded_config = None993        self._popen = popen994 995    def load(self):996        credential_process = self._credential_process997        if credential_process is None:998            return999 1000        creds_dict = self._retrieve_credentials_using(credential_process)1001        if creds_dict.get('expiry_time') is not None:1002            return RefreshableCredentials.create_from_metadata(1003                creds_dict,1004                lambda: self._retrieve_credentials_using(credential_process),1005                self.METHOD,1006            )1007 1008        return Credentials(1009            access_key=creds_dict['access_key'],1010            secret_key=creds_dict['secret_key'],1011            token=creds_dict.get('token'),1012            method=self.METHOD,1013        )1014 1015    def _retrieve_credentials_using(self, credential_process):1016        # We're not using shell=True, so we need to pass the1017        # command and all arguments as a list.1018        process_list = compat_shell_split(credential_process)1019        p = self._popen(1020            process_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE1021        )1022        stdout, stderr = p.communicate()1023        if p.returncode != 0:1024            raise CredentialRetrievalError(1025                provider=self.METHOD, error_msg=stderr.decode('utf-8')1026            )1027        parsed = botocore.compat.json.loads(stdout.decode('utf-8'))1028        version = parsed.get('Version', '<Version key not provided>')1029        if version != 1:1030            raise CredentialRetrievalError(1031                provider=self.METHOD,1032                error_msg=(1033                    f"Unsupported version '{version}' for credential process "1034                    f"provider, supported versions: 1"1035                ),1036            )1037        try:1038            return {1039                'access_key': parsed['AccessKeyId'],1040                'secret_key': parsed['SecretAccessKey'],1041                'token': parsed.get('SessionToken'),1042                'expiry_time': parsed.get('Expiration'),1043            }1044        except KeyError as e:1045            raise CredentialRetrievalError(1046                provider=self.METHOD,1047                error_msg=f"Missing required key in response: {e}",1048            )1049 1050    @property1051    def _credential_process(self):1052        if self._loaded_config is None:1053            self._loaded_config = self._load_config()1054        profile_config = self._loaded_config.get('profiles', {}).get(1055            self._profile_name, {}1056        )1057        return profile_config.get('credential_process')1058 1059 1060class InstanceMetadataProvider(CredentialProvider):1061    METHOD = 'iam-role'1062    CANONICAL_NAME = 'Ec2InstanceMetadata'1063 1064    def __init__(self, iam_role_fetcher):1065        self._role_fetcher = iam_role_fetcher1066 1067    def load(self):1068        fetcher = self._role_fetcher1069        # We do the first request, to see if we get useful data back.1070        # If not, we'll pass & move on to whatever's next in the credential1071        # chain.1072        metadata = fetcher.retrieve_iam_role_credentials()1073        if not metadata:1074            return None1075        logger.info(1076            'Found credentials from IAM Role: %s', metadata['role_name']1077        )1078        # We manually set the data here, since we already made the request &1079        # have it. When the expiry is hit, the credentials will auto-refresh1080        # themselves.1081        creds = RefreshableCredentials.create_from_metadata(1082            metadata,1083            method=self.METHOD,1084            refresh_using=fetcher.retrieve_iam_role_credentials,1085        )1086        return creds1087 1088 1089class EnvProvider(CredentialProvider):1090    METHOD = 'env'1091    CANONICAL_NAME = 'Environment'1092    ACCESS_KEY = 'AWS_ACCESS_KEY_ID'1093    SECRET_KEY = 'AWS_SECRET_ACCESS_KEY'1094    # The token can come from either of these env var.1095    # AWS_SESSION_TOKEN is what other AWS SDKs have standardized on.1096    TOKENS = ['AWS_SECURITY_TOKEN', 'AWS_SESSION_TOKEN']1097    EXPIRY_TIME = 'AWS_CREDENTIAL_EXPIRATION'1098 1099    def __init__(self, environ=None, mapping=None):1100        """1101 1102        :param environ: The environment variables (defaults to1103            ``os.environ`` if no value is provided).1104        :param mapping: An optional mapping of variable names to1105            environment variable names.  Use this if you want to1106            change the mapping of access_key->AWS_ACCESS_KEY_ID, etc.1107            The dict can have up to 3 keys: ``access_key``, ``secret_key``,1108            ``session_token``.1109        """1110        if environ is None:1111            environ = os.environ1112        self.environ = environ1113        self._mapping = self._build_mapping(mapping)1114 1115    def _build_mapping(self, mapping):1116        # Mapping of variable name to env var name.1117        var_mapping = {}1118        if mapping is None:1119            # Use the class var default.1120            var_mapping['access_key'] = self.ACCESS_KEY1121            var_mapping['secret_key'] = self.SECRET_KEY1122            var_mapping['token'] = self.TOKENS1123            var_mapping['expiry_time'] = self.EXPIRY_TIME1124        else:1125            var_mapping['access_key'] = mapping.get(1126                'access_key', self.ACCESS_KEY1127            )1128            var_mapping['secret_key'] = mapping.get(1129                'secret_key', self.SECRET_KEY1130            )1131            var_mapping['token'] = mapping.get('token', self.TOKENS)1132            if not isinstance(var_mapping['token'], list):1133                var_mapping['token'] = [var_mapping['token']]1134            var_mapping['expiry_time'] = mapping.get(1135                'expiry_time', self.EXPIRY_TIME1136            )1137        return var_mapping1138 1139    def load(self):1140        """1141        Search for credentials in explicit environment variables.1142        """1143 1144        access_key = self.environ.get(self._mapping['access_key'], '')1145 1146        if access_key:1147            logger.info('Found credentials in environment variables.')1148            fetcher = self._create_credentials_fetcher()1149            credentials = fetcher(require_expiry=False)1150 1151            expiry_time = credentials['expiry_time']1152            if expiry_time is not None:1153                expiry_time = parse(expiry_time)1154                return RefreshableCredentials(1155                    credentials['access_key'],1156                    credentials['secret_key'],1157                    credentials['token'],1158                    expiry_time,1159                    refresh_using=fetcher,1160                    method=self.METHOD,1161                )1162 1163            return Credentials(1164                credentials['access_key'],1165                credentials['secret_key'],1166                credentials['token'],1167                method=self.METHOD,1168            )1169        else:1170            return None1171 1172    def _create_credentials_fetcher(self):1173        mapping = self._mapping1174        method = self.METHOD1175        environ = self.environ1176 1177        def fetch_credentials(require_expiry=True):1178            credentials = {}1179 1180            access_key = environ.get(mapping['access_key'], '')1181            if not access_key:1182                raise PartialCredentialsError(1183                    provider=method, cred_var=mapping['access_key']1184                )1185            credentials['access_key'] = access_key1186 1187            secret_key = environ.get(mapping['secret_key'], '')1188            if not secret_key:1189                raise PartialCredentialsError(1190                    provider=method, cred_var=mapping['secret_key']1191                )1192            credentials['secret_key'] = secret_key1193 1194            credentials['token'] = None1195            for token_env_var in mapping['token']:1196                token = environ.get(token_env_var, '')1197                if token:1198                    credentials['token'] = token1199                    break1200 

Showing the first 1,200 of 2298 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai