File: C:/Users/fred/anaconda3/Lib/site-packages/aiobotocore/signers.py
import datetime
import botocore
import botocore.auth
from botocore.exceptions import UnknownClientMethodError
from botocore.signers import (
RequestSigner,
S3PostPresigner,
UnknownSignatureVersionError,
UnsupportedSignatureVersionError,
_should_use_global_endpoint,
create_request_object,
prepare_request_dict,
)
from botocore.utils import ArnParser
class AioRequestSigner(RequestSigner):
async def handler(self, operation_name=None, request=None, **kwargs):
# This is typically hooked up to the "request-created" event
# from a client's event emitter. When a new request is created
# this method is invoked to sign the request.
# Don't call this method directly.
return await self.sign(operation_name, request)
async def sign(
self,
operation_name,
request,
region_name=None,
signing_type='standard',
expires_in=None,
signing_name=None,
):
explicit_region_name = region_name
if region_name is None:
region_name = self._region_name
if signing_name is None:
signing_name = self._signing_name
signature_version = await self._choose_signer(
operation_name, signing_type, request.context
)
# Allow mutating request before signing
await self._event_emitter.emit(
'before-sign.{}.{}'.format(
self._service_id.hyphenize(), operation_name
),
request=request,
signing_name=signing_name,
region_name=self._region_name,
signature_version=signature_version,
request_signer=self,
operation_name=operation_name,
)
if signature_version != botocore.UNSIGNED:
kwargs = {
'signing_name': signing_name,
'region_name': region_name,
'signature_version': signature_version,
}
if expires_in is not None:
kwargs['expires'] = expires_in
signing_context = request.context.get('signing', {})
if not explicit_region_name and signing_context.get('region'):
kwargs['region_name'] = signing_context['region']
if signing_context.get('signing_name'):
kwargs['signing_name'] = signing_context['signing_name']
if signing_context.get('identity_cache') is not None:
self._resolve_identity_cache(
kwargs,
signing_context['identity_cache'],
signing_context['cache_key'],
)
try:
auth = await self.get_auth_instance(**kwargs)
except UnknownSignatureVersionError as e:
if signing_type != 'standard':
raise UnsupportedSignatureVersionError(
signature_version=signature_version
)
else:
raise e
auth.add_auth(request)
async def _choose_signer(self, operation_name, signing_type, context):
signing_type_suffix_map = {
'presign-post': '-presign-post',
'presign-url': '-query',
}
suffix = signing_type_suffix_map.get(signing_type, '')
# operation specific signing context takes precedent over client-level
# defaults
signature_version = context.get('auth_type') or self._signature_version
signing = context.get('signing', {})
signing_name = signing.get('signing_name', self._signing_name)
region_name = signing.get('region', self._region_name)
if (
signature_version is not botocore.UNSIGNED
and not signature_version.endswith(suffix)
):
signature_version += suffix
handler, response = await self._event_emitter.emit_until_response(
'choose-signer.{}.{}'.format(
self._service_id.hyphenize(), operation_name
),
signing_name=signing_name,
region_name=region_name,
signature_version=signature_version,
context=context,
)
if response is not None:
signature_version = response
# The suffix needs to be checked again in case we get an improper
# signature version from choose-signer.
if (
signature_version is not botocore.UNSIGNED
and not signature_version.endswith(suffix)
):
signature_version += suffix
return signature_version
async def get_auth_instance(
self, signing_name, region_name, signature_version=None, **kwargs
):
if signature_version is None:
signature_version = self._signature_version
cls = botocore.auth.AUTH_TYPE_MAPS.get(signature_version)
if cls is None:
raise UnknownSignatureVersionError(
signature_version=signature_version
)
if cls.REQUIRES_TOKEN is True:
frozen_token = None
if self._auth_token is not None:
frozen_token = await self._auth_token.get_frozen_token()
auth = cls(frozen_token)
return auth
credentials = self._credentials
if getattr(cls, "REQUIRES_IDENTITY_CACHE", None) is True:
cache = kwargs["identity_cache"]
key = kwargs["cache_key"]
credentials = await cache.get_credentials(key)
del kwargs["cache_key"]
frozen_credentials = None
if credentials is not None:
frozen_credentials = await credentials.get_frozen_credentials()
kwargs['credentials'] = frozen_credentials
if cls.REQUIRES_REGION:
if self._region_name is None:
raise botocore.exceptions.NoRegionError()
kwargs['region_name'] = region_name
kwargs['service_name'] = signing_name
auth = cls(**kwargs)
return auth
# Alias get_auth for backwards compatibility.
get_auth = get_auth_instance
async def generate_presigned_url(
self,
request_dict,
operation_name,
expires_in=3600,
region_name=None,
signing_name=None,
):
request = create_request_object(request_dict)
await self.sign(
operation_name,
request,
region_name,
'presign-url',
expires_in,
signing_name,
)
request.prepare()
return request.url
def add_generate_db_auth_token(class_attributes, **kwargs):
class_attributes['generate_db_auth_token'] = generate_db_auth_token
async def generate_db_auth_token(
self, DBHostname, Port, DBUsername, Region=None
):
"""Generates an auth token used to connect to a db with IAM credentials.
:type DBHostname: str
:param DBHostname: The hostname of the database to connect to.
:type Port: int
:param Port: The port number the database is listening on.
:type DBUsername: str
:param DBUsername: The username to log in as.
:type Region: str
:param Region: The region the database is in. If None, the client
region will be used.
:return: A presigned url which can be used as an auth token.
"""
region = Region
if region is None:
region = self.meta.region_name
params = {
'Action': 'connect',
'DBUser': DBUsername,
}
request_dict = {
'url_path': '/',
'query_string': '',
'headers': {},
'body': params,
'method': 'GET',
}
# RDS requires that the scheme not be set when sent over. This can cause
# issues when signing because the Python url parsing libraries follow
# RFC 1808 closely, which states that a netloc must be introduced by `//`.
# Otherwise the url is presumed to be relative, and thus the whole
# netloc would be treated as a path component. To work around this we
# introduce https here and remove it once we're done processing it.
scheme = 'https://'
endpoint_url = f'{scheme}{DBHostname}:{Port}'
prepare_request_dict(request_dict, endpoint_url)
presigned_url = await self._request_signer.generate_presigned_url(
operation_name='connect',
request_dict=request_dict,
region_name=region,
expires_in=900,
signing_name='rds-db',
)
return presigned_url[len(scheme) :]
class AioS3PostPresigner(S3PostPresigner):
async def generate_presigned_post(
self,
request_dict,
fields=None,
conditions=None,
expires_in=3600,
region_name=None,
):
if fields is None:
fields = {}
if conditions is None:
conditions = []
# Create the policy for the post.
policy = {}
# Create an expiration date for the policy
datetime_now = datetime.datetime.utcnow()
expire_date = datetime_now + datetime.timedelta(seconds=expires_in)
policy['expiration'] = expire_date.strftime(botocore.auth.ISO8601)
# Append all of the conditions that the user supplied.
policy['conditions'] = []
for condition in conditions:
policy['conditions'].append(condition)
# Store the policy and the fields in the request for signing
request = create_request_object(request_dict)
request.context['s3-presign-post-fields'] = fields
request.context['s3-presign-post-policy'] = policy
await self._request_signer.sign(
'PutObject', request, region_name, 'presign-post'
)
# Return the url and the fields for th form to post.
return {'url': request.url, 'fields': fields}
def add_generate_presigned_url(class_attributes, **kwargs):
class_attributes['generate_presigned_url'] = generate_presigned_url
async def generate_presigned_url(
self, ClientMethod, Params=None, ExpiresIn=3600, HttpMethod=None
):
"""Generate a presigned url given a client, its method, and arguments
:type ClientMethod: string
:param ClientMethod: The client method to presign for
:type Params: dict
:param Params: The parameters normally passed to
``ClientMethod``.
:type ExpiresIn: int
:param ExpiresIn: The number of seconds the presigned url is valid
for. By default it expires in an hour (3600 seconds)
:type HttpMethod: string
:param HttpMethod: The http method to use on the generated url. By
default, the http method is whatever is used in the method's model.
:returns: The presigned url
"""
client_method = ClientMethod
params = Params
if params is None:
params = {}
expires_in = ExpiresIn
http_method = HttpMethod
context = {
'is_presign_request': True,
'use_global_endpoint': _should_use_global_endpoint(self),
}
request_signer = self._request_signer
try:
operation_name = self._PY_TO_OP_NAME[client_method]
except KeyError:
raise UnknownClientMethodError(method_name=client_method)
operation_model = self.meta.service_model.operation_model(operation_name)
params = await self._emit_api_params(
api_params=params,
operation_model=operation_model,
context=context,
)
bucket_is_arn = ArnParser.is_arn(params.get('Bucket', ''))
(
endpoint_url,
additional_headers,
properties,
) = await self._resolve_endpoint_ruleset(
operation_model,
params,
context,
ignore_signing_region=(not bucket_is_arn),
)
request_dict = await self._convert_to_request_dict(
api_params=params,
operation_model=operation_model,
endpoint_url=endpoint_url,
context=context,
headers=additional_headers,
set_user_agent_header=False,
)
# Switch out the http method if user specified it.
if http_method is not None:
request_dict['method'] = http_method
# Generate the presigned url.
return await request_signer.generate_presigned_url(
request_dict=request_dict,
expires_in=expires_in,
operation_name=operation_name,
)
def add_generate_presigned_post(class_attributes, **kwargs):
class_attributes['generate_presigned_post'] = generate_presigned_post
async def generate_presigned_post(
self, Bucket, Key, Fields=None, Conditions=None, ExpiresIn=3600
):
bucket = Bucket
key = Key
fields = Fields
conditions = Conditions
expires_in = ExpiresIn
if fields is None:
fields = {}
else:
fields = fields.copy()
if conditions is None:
conditions = []
context = {
'is_presign_request': True,
'use_global_endpoint': _should_use_global_endpoint(self),
}
post_presigner = AioS3PostPresigner(self._request_signer)
# We choose the CreateBucket operation model because its url gets
# serialized to what a presign post requires.
operation_model = self.meta.service_model.operation_model('CreateBucket')
params = await self._emit_api_params(
api_params={'Bucket': bucket},
operation_model=operation_model,
context=context,
)
bucket_is_arn = ArnParser.is_arn(params.get('Bucket', ''))
(
endpoint_url,
additional_headers,
properties,
) = await self._resolve_endpoint_ruleset(
operation_model,
params,
context,
ignore_signing_region=(not bucket_is_arn),
)
request_dict = await self._convert_to_request_dict(
api_params=params,
operation_model=operation_model,
endpoint_url=endpoint_url,
context=context,
headers=additional_headers,
set_user_agent_header=False,
)
# Append that the bucket name to the list of conditions.
conditions.append({'bucket': bucket})
# If the key ends with filename, the only constraint that can be
# imposed is if it starts with the specified prefix.
if key.endswith('${filename}'):
conditions.append(["starts-with", '$key', key[: -len('${filename}')]])
else:
conditions.append({'key': key})
# Add the key to the fields.
fields['key'] = key
return await post_presigner.generate_presigned_post(
request_dict=request_dict,
fields=fields,
conditions=conditions,
expires_in=expires_in,
)