Ejemplo n.º 1
0
def inject_assume_role_provider_cache(session, **kwargs):
    try:
        cred_chain = session.get_component('credential_provider')
    except ProfileNotFound:
        # If a user has provided a profile that does not exist,
        # trying to retrieve components/config on the session
        # will raise ProfileNotFound.  Sometimes this is invalid:
        #
        # "ec2 describe-instances --profile unknown"
        #
        # and sometimes this is perfectly valid:
        #
        # "configure set region us-west-2 --profile brand-new-profile"
        #
        # Because we can't know (and don't want to know) whether
        # the customer is trying to do something valid, we just
        # immediately return.  If it's invalid something else
        # up the stack will raise ProfileNotFound, otherwise
        # the configure (and other) commands will work as expected.
        LOG.debug("ProfileNotFound caught when trying to inject "
                  "assume-role cred provider cache.  Not configuring "
                  "JSONFileCache for assume-role.")
        return
    assume_role_provider = cred_chain.get_provider('assume-role')
    assume_role_provider.cache = JSONFileCache(CACHE_DIR)
    web_identity_provider = cred_chain.get_provider(
        'assume-role-with-web-identity'
    )
    web_identity_provider.cache = JSONFileCache(CACHE_DIR)
Ejemplo n.º 2
0
    def get_accounts_and_roles(self):
        cache = JSONFileCache()
        saml_fetcher = SAMLFetcher(self, cache=cache)

        app_and_role = saml_fetcher.get_app_roles()

        result_accounts = []
        results = {
            "application_url": app_and_role["Application"],
            "accounts": result_accounts,
            "user": app_and_role["User"],
            "organization": app_and_role["Organization"],
        }

        accounts = app_and_role["Accounts"]
        for name_raw in accounts:
            account_parts = re.match(
                r"(Account:) ([a-zA-Z0-9-_]+) \(([0-9]+)\)", name_raw)
            account = account_parts[2]
            account_id = account_parts[3]
            roles = accounts[name_raw]
            result_roles = []
            result_account = {
                "name": account,
                "id": account_id,
                "name_raw": name_raw,
                "roles": result_roles
            }
            result_accounts.append(result_account)
            for role in roles:
                role_suffix = role.split(
                    os.environ.get("AWS_OKTA_ROLE_SUFFIX_DELIMITER", "-"))[-1]
                result_roles.append({"name": role, "suffix": role_suffix})

        return results
def get_token_loader(session,
                     sso_region,
                     interactive=False,
                     token_cache=None,
                     on_pending_authorization=None,
                     force_refresh=False):

    if token_cache is None:
        token_cache = JSONFileCache(SSO_TOKEN_DIR)

    if on_pending_authorization is None:
        if interactive:
            on_pending_authorization = OpenBrowserHandler(
                outfile=sys.stderr,
                open_browser=webbrowser.open_new_tab,
            )
        else:
            on_pending_authorization = _non_interactive_auth_raiser

    token_fetcher = SSOTokenFetcher(
        sso_region=sso_region,
        client_creator=session.create_client,
        cache=token_cache,
        on_pending_authorization=on_pending_authorization,
    )

    def token_loader(start_url):
        token_response = token_fetcher.fetch_token(start_url=start_url,
                                                   force_refresh=force_refresh)
        LOGGER.debug(f'TOKEN RESPONSE: {token_response}')
        return token_response['accessToken']

    return token_loader
Ejemplo n.º 4
0
        def __init__(self,
                     name,
                     access_key=None,
                     secret_key=None,
                     security_token=None,
                     profile_name=None,
                     **kwargs):
            """
            Create a new BotoCredentialAdapter.
            """
            # TODO: We take kwargs because new boto2 versions have an 'anon'
            # argument and we want to be future proof

            if (name == 'aws'
                    or name is None) and access_key is None and not kwargs.get(
                        'anon', False):
                # We are on AWS and we don't have credentials passed along and we aren't anonymous.
                # We will backend into a boto3 resolver for getting credentials.
                # Make sure to enable boto3's own caching, so we can share that
                # cash with pure boto3 code elsewhere in Toil.
                self._boto3_resolver = create_credential_resolver(
                    Session(profile=profile_name), cache=JSONFileCache())
            else:
                # We will use the normal flow
                self._boto3_resolver = None

            # Pass along all the arguments
            super(BotoCredentialAdapter,
                  self).__init__(name,
                                 access_key=access_key,
                                 secret_key=secret_key,
                                 security_token=security_token,
                                 profile_name=profile_name,
                                 **kwargs)
Ejemplo n.º 5
0
def get_credentials(session,
                    start_url,
                    sso_region,
                    account_id,
                    role_name,
                    token_fetcher=None,
                    force_refresh=False,
                    cache=None):
    if hasattr(session, "_session"):  #boto3 Session
        session = session._session
    if not token_fetcher:
        token_fetcher = get_token_fetcher(session, sso_region)

    token_loader = _get_token_loader_from_token_fetcher(
        token_fetcher=token_fetcher, force_refresh=force_refresh)

    if cache is None:
        cache = JSONFileCache(CREDENTIALS_CACHE_DIR)

    credential_fetcher = SSOCredentialFetcher(
        start_url=start_url,
        sso_region=sso_region,
        role_name=role_name,
        account_id=account_id,
        client_creator=session.create_client,
        cache=cache,
        token_loader=token_loader,
    )

    return credential_fetcher.fetch_credentials()
Ejemplo n.º 6
0
def inject_json_file_cache(session, **kwargs):
    try:
        cred_chain = session.get_component('credential_provider')
        sso_provider = cred_chain.get_provider('sso')
        sso_provider.cache = JSONFileCache(AWS_CREDS_CACHE_DIR)
    except (ProfileNotFound, UnknownCredentialError):
        return
Ejemplo n.º 7
0
def get_token_fetcher(session, sso_region, *, interactive=False, sso_cache=None,
                     on_pending_authorization=None, message=None, outfile=None,
                     disable_browser=None, expiry_window=None):
    if hasattr(session, "_session"): #boto3 Session
        session = session._session

    if sso_cache is None:
        sso_cache = JSONFileCache(SSO_TOKEN_DIR, dumps_func=_sso_json_dumps)

    if on_pending_authorization is None:
        if interactive:
            on_pending_authorization = OpenBrowserHandler(
                outfile=outfile,
                message=message,
                disable_browser=disable_browser,
            )
        else:
            on_pending_authorization = non_interactive_auth_raiser

    token_fetcher = SSOTokenFetcher(
        sso_region=sso_region,
        client_creator=session.create_client,
        cache=sso_cache,
        on_pending_authorization=on_pending_authorization,
        expiry_window=expiry_window,
    )
    return token_fetcher
Ejemplo n.º 8
0
    def authenticate(self):
        cache = JSONFileCache()
        saml_fetcher = SAMLFetcher(self, cache=cache)

        credentials = saml_fetcher.fetch_credentials()

        return credentials
Ejemplo n.º 9
0
def get_credentials(session, start_url, sso_region, account_id, role_name, *,
        token_fetcher=None,
        force_refresh=False,
        sso_cache=None,
        credential_cache=None):
    """Return credentials for the given role.

    The return value is a dict containing the assumed role credentials.

    You probably want to use get_boto3_session() instead, which returns
    a boto3 session that caches its credentials and automatically refreshes them.
    """
    if hasattr(session, "_session"): #boto3 Session
        session = session._session
    if not token_fetcher:
        token_fetcher = get_token_fetcher(session, sso_region, sso_cache=sso_cache)

    token_loader = _get_token_loader_from_token_fetcher(token_fetcher=token_fetcher, force_refresh=force_refresh)

    if credential_cache is None:
        credential_cache = JSONFileCache(CREDENTIALS_CACHE_DIR)

    credential_fetcher = SSOCredentialFetcher(
        start_url=start_url,
        sso_region=sso_region,
        role_name=role_name,
        account_id=account_id,
        client_creator=session.create_client,
        cache=credential_cache,
        token_loader=token_loader,
    )

    return credential_fetcher.fetch_credentials()
Ejemplo n.º 10
0
 def load(self):
     self._loaded_config = self._load_config()
     profiles = self._loaded_config.get('profiles', {})
     profile = profiles.get(self._profile_name, {})
     tmp_config_file = os.path.expanduser(
         profile.get(self.TMP_SESSION_CREDENTIAL_FILE_VAR,
                     DEFAULT_TMP_CREDENTIAL_FILE))
     if self.cache is None and self._enable_cache_fallback:
         if tmp_config_file:
             self.cache = SimpleMFACache(
                 TempConfigWriter(tmp_config_file, self._profile_name,
                                  profile.get('region')),
                 JSONFileCache(CACHE_DIR))
         else:
             self.cache = JSONFileCache(CACHE_DIR)
     if self._has_mfa_config_vars(profile):
         return self._load_creds(profile)
Ejemplo n.º 11
0
    def test_sso_source_profile(self):
        token_cache_key = 'f395038c92f1828cbb3991d2d6152d326b895606'
        cached_token = {
            'accessToken': 'a.token',
            'expiresAt': self.some_future_time(),
        }
        temp_cache = JSONFileCache(self.tempdir)
        temp_cache[token_cache_key] = cached_token

        config = ('[profile A]\n'
                  'role_arn = arn:aws:iam::123456789:role/RoleA\n'
                  'source_profile = B\n'
                  '[profile B]\n'
                  'sso_region = us-east-1\n'
                  'sso_start_url = https://test.url/start\n'
                  'sso_role_name = SSORole\n'
                  'sso_account_id = 1234567890\n')
        self.write_config(config)

        session, sts_stubber = self.create_session(profile='A')
        client_config = Config(
            region_name='us-east-1',
            signature_version=UNSIGNED,
        )
        sso_stubber = session.stub('sso', config=client_config)
        sso_stubber.activate()
        # The expiration needs to be in milliseconds
        expiration = datetime2timestamp(self.some_future_time()) * 1000
        sso_role_creds = self.create_random_credentials()
        sso_role_response = {
            'roleCredentials': {
                'accessKeyId': sso_role_creds.access_key,
                'secretAccessKey': sso_role_creds.secret_key,
                'sessionToken': sso_role_creds.token,
                'expiration': int(expiration),
            }
        }
        sso_stubber.add_response('get_role_credentials', sso_role_response)

        expected_creds = self.create_random_credentials()
        assume_role_response = self.create_assume_role_response(expected_creds)
        sts_stubber.add_response('assume_role', assume_role_response)

        actual_creds = session.get_credentials()
        self.assert_creds_equal(actual_creds, expected_creds)
        sts_stubber.assert_no_pending_responses()
        # Assert that the client was created with the credentials from the
        # SSO get role credentials response
        self.assertEqual(self.mock_client_creator.call_count, 1)
        _, kwargs = self.mock_client_creator.call_args_list[0]
        expected_kwargs = {
            'aws_access_key_id': sso_role_creds.access_key,
            'aws_secret_access_key': sso_role_creds.secret_key,
            'aws_session_token': sso_role_creds.token,
        }
        self.assertEqual(kwargs, expected_kwargs)
Ejemplo n.º 12
0
    def create_session(self, profile=None):
        session = StubbedSession(profile=profile)

        # We have to set bogus credentials here or otherwise we'll trigger
        # an early credential chain resolution.
        sts = session.create_client(
            'sts',
            aws_access_key_id='spam',
            aws_secret_access_key='eggs',
        )
        self.mock_client_creator.return_value = sts
        assume_role_provider = AssumeRoleProvider(
            load_config=lambda: session.full_config,
            client_creator=self.mock_client_creator,
            cache={},
            profile_name=profile,
            credential_sourcer=CanonicalNameCredentialSourcer([
                self.env_provider, self.container_provider,
                self.metadata_provider
            ]),
            profile_provider_builder=ProfileProviderBuilder(
                session,
                sso_token_cache=JSONFileCache(self.tempdir),
            ),
        )
        stubber = session.stub('sts')
        stubber.activate()

        component_name = 'credential_provider'
        resolver = session.get_component(component_name)
        available_methods = [p.METHOD for p in resolver.providers]
        replacements = {
            'env': self.env_provider,
            'iam-role': self.metadata_provider,
            'container-role': self.container_provider,
            'assume-role': assume_role_provider
        }
        for name, provider in replacements.items():
            try:
                index = available_methods.index(name)
            except ValueError:
                # The provider isn't in the session
                continue

            resolver.providers[index] = provider

        session.register_component('credential_provider', resolver)
        return session, stubber
Ejemplo n.º 13
0
 def __init__(self,
              load_config,
              client_creator,
              profile_name,
              cache=None,
              token_cache=None):
     """Instantiate class."""
     if token_cache is None:
         token_cache = JSONFileCache(self._SSO_TOKEN_CACHE_DIR)
     self._token_cache = token_cache
     if cache is None:
         cache = {}
     self.cache = cache
     self._load_config = load_config
     self._client_creator = client_creator
     self._profile_name = profile_name
Ejemplo n.º 14
0
Archivo: ec2.py Proyecto: douglowe/toil
def establish_boto3_session(region_name: Optional[str] = None) -> Session:
    """
    This is the One True Place where Boto3 sessions should be established, and
    prepares them with the necessary credential caching.

    :param region_name: If given, the session will be associated with the given AWS region.
    """

    # Make sure to use credential caching when talking to Amazon via boto3
    # See https://github.com/boto/botocore/pull/1338/
    # And https://github.com/boto/botocore/commit/2dae76f52ae63db3304b5933730ea5efaaaf2bfc

    botocore_session = get_session()
    botocore_session.get_component('credential_provider').get_provider(
        'assume-role').cache = JSONFileCache()
    return Session(botocore_session=botocore_session, region_name=region_name)
Ejemplo n.º 15
0
def do_sso_login(session,
                 sso_region,
                 start_url,
                 token_cache=None,
                 on_pending_authorization=None,
                 force_refresh=False):
    if token_cache is None:
        token_cache = JSONFileCache(SSO_TOKEN_DIR)
    if on_pending_authorization is None:
        on_pending_authorization = OpenBrowserHandler(
            open_browser=open_browser_with_original_ld_path)
    token_fetcher = SSOTokenFetcher(
        sso_region=sso_region,
        client_creator=session.create_client,
        cache=token_cache,
        on_pending_authorization=on_pending_authorization)
    return token_fetcher.fetch_token(start_url=start_url,
                                     force_refresh=force_refresh)
Ejemplo n.º 16
0
    def run(self):
        cache = JSONFileCache()
        saml_fetcher = SAMLFetcher(self, cache=cache)

        credentials = saml_fetcher.fetch_credentials()

        if self.configuration["AWS_OKTA_ENVIRONMENT"]:
            if os.name == 'nt':
                print(
                    NT_EXPORT_STRING.format(credentials["AccessKeyId"],
                                            credentials["SecretAccessKey"],
                                            credentials["SessionToken"]))
            else:
                print(
                    UNIX_EXPORT_STRING.format(credentials["AccessKeyId"],
                                              credentials["SecretAccessKey"],
                                              credentials["SessionToken"]))
        else:
            credentials["Version"] = 1
            print(json.dumps(credentials))
Ejemplo n.º 17
0
def setup_aws_client(config):
    role_arn = "arn:aws:iam::{}:role/{}".format(
        config['account_id'].replace('-', ''), config['role_name'])
    session = Session()
    fetcher = AssumeRoleCredentialFetcher(session.create_client,
                                          session.get_credentials(),
                                          role_arn,
                                          extra_args={
                                              'DurationSeconds': 3600,
                                              'RoleSessionName': 'TapS3CSV',
                                              'ExternalId':
                                              config['external_id']
                                          },
                                          cache=JSONFileCache())

    refreshable_session = Session()
    refreshable_session.register_component(
        'credential_provider',
        CredentialResolver([AssumeRoleProvider(fetcher)]))

    LOGGER.info("Attempting to assume_role on RoleArn: %s", role_arn)
    boto3.setup_default_session(botocore_session=refreshable_session)
def get_credentials(session,
                    sso_region,
                    start_url,
                    account_id,
                    role_name,
                    token_loader,
                    cache=None):

    if cache is None:
        cache = JSONFileCache(SSO_TOKEN_DIR)

    credential_fetcher = SSOCredentialFetcher(
        start_url=start_url,
        sso_region=sso_region,
        role_name=role_name,
        account_id=account_id,
        client_creator=session.create_client,
        cache=cache,
        token_loader=token_loader,
    )

    return credential_fetcher.fetch_credentials()
def inject_mfa_credential_provider(session, **kwargs):
    try:
        credential_provider = session.get_component('credential_provider')
    except ProfileNotFound:
        # If a user has provided a profile that does not exist,
        # trying to retrieve components/config on the session
        # will raise ProfileNotFound.  Sometimes this is invalid:
        #
        # "ec2 describe-instances --profile unknown"
        #
        # and sometimes this is perfectly valid:
        #
        # "configure set region us-west-2 --profile brand-new-profile"
        #
        # Because we can't know (and don't want to know) whether
        # the customer is trying to do something valid, we just
        # immediately return.  If it's invalid something else
        # up the stack will raise ProfileNotFound, otherwise
        # the configure (and other) commands will work as expected.
        logger.debug(
            'ProfileNotFound caught when trying to inject mfa credential provider. '
            'Credential provider not configured.')
        return

    credential_provider.insert_after(
        'env',
        MfaCredentialProvider(
            load_config=lambda: session.full_config,
            client_creator=session.create_client,
            cache=JSONFileCache(),
            profile_name=session.get_config_variable('profile') or 'default',
            credential_sourcer=CanonicalNameCredentialSourcer([
                credential_provider.get_provider('env'),
                credential_provider.get_provider('shared-credentials-file'),
                credential_provider.get_provider('config-file'),
            ]),
        ),
    )
Ejemplo n.º 20
0
    def _get_boto3_session(region: str,
                           role_arn: str = None,
                           assume_duration: int = 3600) -> Session:
        """Creates a boto3 session, optionally assuming a role.

        Args:
            region: The AWS region for the session.
            role_arn: The ARN to assume for the session.
            assume_duration: The duration (in seconds) to assume the role.

        Returns:
            object: A boto3 Session.
        """

        # By default return a basic session
        if not role_arn:
            return Session(region_name=region)

        # The following assume role example was taken from
        # https://github.com/boto/botocore/issues/761#issuecomment-426037853

        # Create a session used to assume role
        assume_session = BotocoreSession()
        fetcher = AssumeRoleCredentialFetcher(
            assume_session.create_client,
            assume_session.get_credentials(),
            role_arn,
            extra_args={
                "DurationSeconds": assume_duration,
            },
            cache=JSONFileCache(),
        )
        role_session = BotocoreSession()
        role_session.register_component(
            "credential_provider",
            CredentialResolver([Boto3Manager.AssumeRoleProvider(fetcher)]),
        )
        return Session(region_name=region, botocore_session=role_session)
Ejemplo n.º 21
0
def setup_aws_client(config):
    role_arn = "arn:aws:iam::{}:role/{}".format(
        config["account_id"].replace("-", ""), config["role_name"])
    session = Session()
    fetcher = AssumeRoleCredentialFetcher(
        session.create_client,
        session.get_credentials(),
        role_arn,
        extra_args={
            "DurationSeconds": 3600,
            "RoleSessionName": "TapS3CSV",
            "ExternalId": config["external_id"],
        },
        cache=JSONFileCache(),
    )

    refreshable_session = Session()
    refreshable_session.register_component(
        "credential_provider",
        CredentialResolver([AssumeRoleProvider(fetcher)]))

    LOGGER.info("Attempting to assume_role on RoleArn: %s", role_arn)
    boto3.setup_default_session(botocore_session=refreshable_session)
Ejemplo n.º 22
0
def get_credential_cache(prog: str):
    """
    Returns a dict-like object for caching credentials on-disk. This is
    similar to how the AWS CLI does things with `~/.aws/cli/cache/`, but
    unfortunately stored at a different location such that neither tool
    benefits from the other's cache.

    There's another way to cache credentials by overriding the `cache`
    attribute on the `assume-role` provider of the `credential_provider`
    component (see <https://github.com/boto/botocore/pull/1157>), but
    that cannot be leveraged here because

    - we are the ones doing the call to `assume_role()` rather than it
      being called for us by another AWS service client, and
    - we want to be able to inspect the `Expiration` value of what we
      have cached to make sure the caller has the requisite time left.
    """

    from appdirs import user_cache_dir
    from botocore.credentials import JSONFileCache

    our_cache_dir = user_cache_dir(appname=prog)  # e.g. ~/.cache/aws-as-role/
    credential_cache = JSONFileCache(working_dir=our_cache_dir)
    return cast(Dict[str, StsCredentials], credential_cache)
Ejemplo n.º 23
0
def setup_aws_client(config):
    if 'role_name' in config:
        role_arn = "arn:aws:iam::{}:role/{}".format(
            config['account_id'].replace('-', ''), config['role_name'])

        session = Session()
        fetcher = AssumeRoleCredentialFetcher(session.create_client,
                                              session.get_credentials(),
                                              role_arn,
                                              extra_args={
                                                  'DurationSeconds':
                                                  3600,
                                                  'RoleSessionName':
                                                  'TapDynamodDB',
                                                  'ExternalId':
                                                  config['external_id']
                                              },
                                              cache=JSONFileCache())

        refreshable_session = Session()
        refreshable_session.register_component(
            'credential_provider',
            CredentialResolver([AssumeRoleProvider(fetcher)]))

        LOGGER.info("Attempting to assume_role on RoleArn: %s", role_arn)
        boto3.setup_default_session(botocore_session=refreshable_session)

    elif 'aws_access_key_id' in config and 'aws_secret_access_key' in config:
        LOGGER.info(
            "Attempting to pass AWS credentials from 'aws_access_key_id' and 'aws_secret_access_key' config values"
        )
        boto3.setup_default_session(
            aws_access_key_id=config['aws_access_key_id'],
            aws_secret_access_key=config['aws_secret_access_key'],
            aws_session_token=config.get('aws_session_token', None))
        session = Session()
Ejemplo n.º 24
0
import json
import os
import sys

from boto3 import Session
from botocore.credentials import CachedCredentialFetcher, CredentialProvider, DeferredRefreshableCredentials, JSONFileCache
from botocore.exceptions import InvalidConfigError, ProfileNotFound
from botocore.session import Session as BotocoreSession

from getpass import getpass

from hashlib import sha1


cache_dir = os.path.expanduser(os.path.join('~', '.aws', 'boto-source-profile-mfa', 'cache'))
cache = JSONFileCache(cache_dir)


class SourceProfileMfaCredentialsFetcher(CachedCredentialFetcher):
    """
    Fetches credentials for a temporary session, prompting for MFA,
    and caches the results to reduce the number of MFA prompts.

    """

    def __init__(self, profile, mfa_serial, mfa_prompter, cache, expiry_window_seconds=60):
        self._profile = profile
        self._mfa_serial = mfa_serial
        self._mfa_prompter = mfa_prompter
        super(SourceProfileMfaCredentialsFetcher, self).__init__(
            cache=cache,
Ejemplo n.º 25
0
def lookup(type, value, instance_arn, identity_store_id, profile,
           error_if_not_found, show_id, separator, header,
           permission_set_style, verbose):
    """Look up names and ids in AWS SSO"""
    configure_logging(LOGGER, verbose)

    session = boto3.Session(profile_name=profile)

    cache = JSONFileCache(IDS_CACHE_DIR)

    ids = _lookup.Ids(session, instance_arn, identity_store_id, cache=cache)
    ids.print_on_fetch = show_id

    HEADER_FIELDS = {
        "group": ["Name", "Id"],
        "user": ["Name", "Id"],
        "permission-set": ["Name", permission_set_style.upper()],
    }

    printer = Printer(
        separator=separator,
        default_separator=" ",
        header_fields=HEADER_FIELDS.get(type),
        disable_header=header,
    )

    try:
        if type == "instance":
            ids.print_on_fetch = False
            print(ids.instance_arn)
        elif type == "identity-store":
            ids.print_on_fetch = False
            print(ids.identity_store_id)
        elif type in "group":
            if not value:
                raise click.UsageError("Group name is required")
            lookup_groups(session,
                          ids,
                          value,
                          printer,
                          error_if_not_found=error_if_not_found)
        elif type == "user":
            if not value:
                raise click.UsageError("User name is required")
            lookup_users(session,
                         ids,
                         value,
                         printer,
                         error_if_not_found=error_if_not_found)
        elif type == "permission-set":
            if not value:
                raise click.UsageError("Permission set name is required")
            if len(value) == 1 and value[0] == ":all":
                lookup_all_permission_sets(
                    session,
                    ids,
                    printer,
                    permission_set_style=permission_set_style)
            else:
                lookup_permission_sets(
                    session,
                    ids,
                    value,
                    printer,
                    permission_set_style=permission_set_style,
                    error_if_not_found=error_if_not_found)

    except _lookup.LookupError as e:
        print(e, file=sys.stderr)
        sys.exit(1)
Ejemplo n.º 26
0
  def from_url(remote_url):
    """
    Parses repository information from a git url, filling in additional
    attributes we need from our AWS profile.

    Our remote helper accepts two distinct types of urls...

    * codecommit://<profile>@<repository>
    * codecommit::<region>://<profile>@<repository>

    If provided the former we get the whole url, but if the later git will
    truncate the proceeding 'codecommit::' prefix for us.

    The '<profile>@' url is optional, using the aws sessions present profile
    if not provided.

    :param str remote_url: git remote url to parse

    :returns: **Context** with our CodeCommit repository information

    :raises:
      * **FormatError** if the url is malformed
      * **ProfileNotFound** if the url references a profile that doesn't exist
      * **RegionNotFound** if the url references a region that doesn't exist
      * **RegionNotAvailable** if the url references a region that is not available
    """

    url = urlparse(remote_url)
    event_handler = botocore.hooks.HierarchicalEmitter()
    profile = 'default'
    repository = url.netloc
    if not url.scheme or not url.netloc:
      raise FormatError('The following URL is malformed: {}. A URL must be in one of the two following formats: codecommit://<profile>@<repository> or codecommit::<region>://<profile>@<repository>'.format(remote_url))

    if '@' in url.netloc:
      profile, repository = url.netloc.split('@', 1)
      session = botocore.session.Session(profile = profile, event_hooks = event_handler)

      if profile not in session.available_profiles:
        raise ProfileNotFound('The following profile was not found: {}. Available profiles are: {}. Either use one of the available profiles, or create an AWS CLI profile to use and then try again. For more information, see Configure an AWS CLI Profile in the AWS CLI User Guide.'.format(profile, ', '.join(session.available_profiles)))
    else:
      session = botocore.session.Session(event_hooks = event_handler)

    session.get_component('credential_provider').get_provider('assume-role').cache = JSONFileCache()

    try:
      # when the aws cli is available support plugin authentication

      import awscli.plugin

      awscli.plugin.load_plugins(
          session.full_config.get('plugins', {}),
          event_hooks = event_handler,
          include_builtins = False,
      )

      session.emit_first_non_none_response('session-initialized', session = session)
    except ImportError:
      pass

    available_regions = [region for partition in session.get_available_partitions() for region in session.get_available_regions('codecommit', partition)]

    if url.scheme == 'codecommit':
      region = session.get_config_variable('region')

      if not region:
        raise RegionNotFound('The following profile does not have an AWS Region: {}. You must set an AWS Region for this profile. For more information, see Configure An AWS CLI Profile in the AWS CLI User Guide.'.format(profile))

      if region not in available_regions:
        raise RegionNotAvailable('The following AWS Region is not available for use with AWS CodeCommit: {}. For more information about CodeCommit\'s availability in AWS Regions, see the AWS CodeCommit User Guide. If an AWS Region is listed as supported but you receive this error, try updating your version of the AWS CLI or the AWS SDKs.'.format(region))

    elif re.match(r"^[a-z]{2}-\w*.*-\d{1}", url.scheme):
      if url.scheme in available_regions:
        region = url.scheme

      else:
        raise RegionNotAvailable('The following AWS Region is not available for use with AWS CodeCommit: {}. For more information about CodeCommit\'s availability in AWS Regions, see the AWS CodeCommit User Guide. If an AWS Region is listed as supported but you receive this error, try updating your version of the AWS CLI or the AWS SDKs.'.format(url.scheme))

    else:
      raise FormatError('The following URL is malformed: {}. A URL must be in one of the two following formats: codecommit://<profile>@<repository> or codecommit::<region>://<profile>@<repository>'.format(remote_url))
    credentials = session.get_credentials()

    if not credentials:
      raise CredentialsNotFound('The following profile does not have credentials configured: {}. You must configure the access key and secret key for the profile. For more information, see Configure an AWS CLI Profile in the AWS CLI User Guide.'.format(profile))

    return Context(session, repository, 'v1', region, credentials)