class Client(BaseClient): boto3_client = None grantless_scope: str = '' keep_restricted_data_token: bool = False def __init__(self, marketplace: Marketplaces = Marketplaces[os.environ.get( 'SP_API_DEFAULT_MARKETPLACE', Marketplaces.US.name)], *, refresh_token=None, account='default', credentials=None, restricted_data_token=None, proxies=None): self.credentials = CredentialProvider(account, credentials).credentials session = boto3.session.Session() self.boto3_client = session.client( 'sts', aws_access_key_id=self.credentials.aws_access_key, aws_secret_access_key=self.credentials.aws_secret_key) self.endpoint = marketplace.endpoint self.marketplace_id = marketplace.marketplace_id self.region = marketplace.region self.restricted_data_token = restricted_data_token self._auth = AccessTokenClient(refresh_token=refresh_token, credentials=self.credentials) self.proxies = proxies def _get_cache_key(self, token_flavor=''): return 'role_' + hashlib.md5( (token_flavor + self._auth.cred.refresh_token).encode('utf-8')).hexdigest() def set_role(self, cache_key='role'): role = self.boto3_client.assume_role(RoleArn=self.credentials.role_arn, RoleSessionName='guid') role_cache[cache_key] = role return role @property def headers(self): return { 'host': self.endpoint[8:], 'user-agent': self.user_agent, 'x-amz-access-token': self.restricted_data_token or self.auth.access_token, 'x-amz-date': datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'), 'content-type': 'application/json' } @property def auth(self) -> AccessTokenResponse: return self._auth.get_auth() @property def grantless_auth(self) -> AccessTokenResponse: if not self.grantless_scope: raise MissingScopeException("Grantless operations require scope") return self._auth.get_grantless_auth(self.grantless_scope) @property def role(self): cache_key = self._get_cache_key() try: role = role_cache[cache_key] except KeyError: role = self.set_role(cache_key) return role.get('Credentials') def _sign_request(self): aws_session_token = None aws_access_key_id = self.credentials.aws_access_key aws_secret_access_key = self.credentials.aws_secret_key if self.credentials.role_arn: role = self.role aws_session_token = role.get('SessionToken') aws_access_key_id = role.get('AccessKeyId') aws_secret_access_key = role.get('SecretAccessKey') return AWSSigV4('execute-api', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, region=self.region, aws_session_token=aws_session_token) def _request(self, path: str, *, data: dict = None, params: dict = None, headers=None, add_marketplace=True) -> ApiResponse: if params is None: params = {} if data is None: data = {} self.method = params.pop('method', data.pop('method', 'GET')) if add_marketplace: self._add_marketplaces(data if self.method in ('POST', 'PUT') else params) res = request(self.method, self.endpoint + path, params=params, data=json.dumps(data) if data and self.method in ('POST', 'PUT', 'PATCH') else None, headers=headers or self.headers, auth=self._sign_request(), proxies=self.proxies) return self._check_response(res) def _check_response(self, res) -> ApiResponse: if self.method == 'DELETE' and 200 <= res.status_code < 300: try: js = res.json() or {} except JSONDecodeError: js = {'status_code': res.status_code} else: js = res.json() or {} if isinstance(js, list): js = js[0] error = js.get('errors', None) if error: exception = get_exception_for_code(res.status_code) raise exception(error, headers=res.headers) return ApiResponse(**js, headers=res.headers) def _add_marketplaces(self, data): POST = ['marketplaceIds', 'MarketplaceIds'] GET = [ 'MarketplaceId', 'MarketplaceIds', 'marketplace_ids', 'marketplaceIds' ] if self.method == 'POST': if any(x in data.keys() for x in POST): return return data.update({ k: self.marketplace_id if not k.endswith('s') else [self.marketplace_id] for k in POST }) if any(x in data.keys() for x in GET): return return data.update({ k: self.marketplace_id if not k.endswith('s') else [self.marketplace_id] for k in GET }) def _request_grantless_operation(self, path: str, *, data: dict = None, params: dict = None): headers = { 'host': self.endpoint[8:], 'user-agent': self.user_agent, 'x-amz-access-token': self.grantless_auth.access_token, 'x-amz-date': datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'), 'content-type': 'application/json' } return self._request(path, data=data, params=params, headers=headers) def __enter__(self): self.keep_restricted_data_token = True return self def __exit__(self, *args, **kwargs): self.restricted_data_token = None self.keep_restricted_data_token = False
class Client(BaseClient): boto3_client = None def __init__( self, marketplace: Marketplaces = Marketplaces.US, *, refresh_token=None, account='default', credentials=None ): super().__init__(account, credentials) self.boto3_client = boto3.client( 'sts', aws_access_key_id=self.credentials.aws_access_key, aws_secret_access_key=self.credentials.aws_secret_key ) self.endpoint = marketplace.endpoint self.marketplace_id = marketplace.marketplace_id self.region = marketplace.region self._auth = AccessTokenClient(refresh_token=refresh_token, account=account, credentials=credentials) def set_role(self): role = self.boto3_client.assume_role( RoleArn=self.credentials.role_arn, RoleSessionName='guid' ) role_cache['role'] = role return role @property def headers(self): return { 'host': self.endpoint[8:], 'user-agent': self.user_agent, 'x-amz-access-token': self.auth.access_token, 'x-amz-date': datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'), 'content-type': 'application/json' } @property def auth(self) -> AccessTokenResponse: return self._auth.get_auth() @property def grantless_auth(self) -> AccessTokenResponse: return self._auth.get_grantless_auth() @property def role(self): try: role = role_cache['role'] except KeyError: role = self.set_role() return role.get('Credentials') def _sign_request(self): role = self.role return AWSSigV4('execute-api', aws_access_key_id=role.get('AccessKeyId'), aws_secret_access_key=role.get('SecretAccessKey'), region=self.region, aws_session_token=role.get('SessionToken') ) def _request(self, path: str, *, data: dict = None, params: dict = None, headers=None, add_marketplace=True) -> ApiResponse: if params is None: params = {} if data is None: data = {} self.method = params.pop('method', data.pop('method', 'GET')) if add_marketplace: self._add_marketplaces(data if self.method == 'POST' else params) if self.method == 'POST': data = json.dumps(data) res = request(self.method, self.endpoint + path, params=params, data=data, headers=headers or self.headers, auth=self._sign_request()) return self._check_response(res) @staticmethod def _check_response(res) -> ApiResponse: error = res.json().get('errors', None) if error: exception = get_exception_for_code(res.status_code) raise exception(error) return ApiResponse(**res.json(), headers=res.headers) def _add_marketplaces(self, data): POST = ['marketplaceIds', 'MarketplaceIds'] GET = ['MarketplaceId', 'MarketplaceIds', 'marketplace_ids', 'marketplaceIds'] if self.method == 'POST': if any(x in data.keys() for x in POST): return return data.update({k: self.marketplace_id for k in POST}) if any(x in data.keys() for x in GET): return return data.update({k: self.marketplace_id for k in GET}) def _request_grantless_operation(self, path: str, *, data: dict = None, params: dict = None): headers = { 'host': self.endpoint[8:], 'user-agent': self.user_agent, 'x-amz-access-token': self.grantless_auth.access_token, 'x-amz-date': datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'), 'content-type': 'application/json' } return self._request(path, data=data, params=params, headers=headers)
class Client(BaseClient): def __init__(self, marketplace: Marketplaces, refresh_token=None): self.endpoint = marketplace.endpoint self.marketplace_id = marketplace.marketplace_id self._auth = AccessTokenClient(refresh_token) super().__init__() @staticmethod def set_role(): role = client.assume_role( RoleArn=os.environ.get('SP_API_ROLE_ARN'), RoleSessionName='guid' ) role_cache['role'] = role return role @property def headers(self): return { 'host': self.endpoint[8:], 'user-agent': self.user_agent, 'x-amz-access-token': self.auth.access_token, 'x-amz-date': datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'), 'content-type': 'application/json' } @property def auth(self) -> AccessTokenResponse: return self._auth.get_auth() @property def grantless_auth(self) -> AccessTokenResponse: return self._auth.get_grantless_auth() @property def role(self): try: role = role_cache['role'] except KeyError: role = self.set_role() return role.get('Credentials') def _sign_request(self): role = self.role return AWSSigV4('execute-api', aws_access_key_id=role.get('AccessKeyId'), aws_secret_access_key=role.get('SecretAccessKey'), region=os.environ.get('SP_AWS_REGION', 'us-east-1'), aws_session_token=role.get('SessionToken') ) def _request(self, path: str, *, data: dict = None, params: dict = None, headers=None, add_marketplace=True): if params is None: params = {} if data is None: data = {} self.method = params.pop('method', data.pop('method', 'GET')) if self.method == 'POST': if add_marketplace and (not data.get('marketplaceIds', None) and not data.get('MarketplaceIds', None)): data.update({'marketplaceIds': [self.marketplace_id], 'MarketplaceIds': [self.marketplace_id]}) data = json.dumps(data) else: if add_marketplace and ('MarketplaceIds' not in params and 'marketplaceIds' not in params and 'marketplace_ids' not in params and 'MarketplaceId' not in params): params.update({'MarketplaceId': self.marketplace_id, 'MarketplaceIds': self.marketplace_id, 'marketplace_ids': self.marketplace_id, 'marketplaceIds': self.marketplace_id}) res = request(self.method, self.endpoint + path, params=params, data=data, headers=headers or self.headers, auth=self._sign_request()) e = res.json().get('errors', None) if e: raise SellingApiException(e) return res def _request_grantless_operation(self, path: str, *, data: dict = None, params: dict = None): headers = { 'host': self.endpoint[8:], 'user-agent': self.user_agent, 'x-amz-access-token': self.grantless_auth.access_token, 'x-amz-date': datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'), 'content-type': 'application/json' } return self._request(path, data=data, params=params, headers=headers)