async def stripe_request(app, auth, method, path, *, idempotency_key=None, **data): client: ClientSession = app['stripe_client'] settings: Settings = app['settings'] metadata = data.pop('metadata', None) if metadata: data.update({f'metadata[{k}]': v for k, v in metadata.items()}) headers = {} if idempotency_key: headers[ 'Idempotency-Key'] = idempotency_key + settings.stripe_idempotency_extra full_path = settings.stripe_root + path async with client.request(method, full_path, data=data or None, auth=auth, headers=headers) as r: if r.status == 200: return await r.json() else: # check stripe > developer > logs for more info text = await r.text() raise RequestError(r.status, full_path, info=text)
async def google_get_details(m: GoogleSiwModel, app): settings: Settings = app['settings'] async with app['http_client'].get(settings.google_siw_url) as r: if r.status != 200: raise RequestError(r.status, settings.google_siw_url, info=await r.text()) certs = await r.json() try: id_info = google_jwt.decode(m.id_token, certs=certs, audience=settings.google_siw_client_key) except ValueError as e: logger.warning('google jwt decode error: %s', e) raise JsonErrors.HTTPBadRequest(message='google jwt decode error') # this should happen very rarely, if it does someone is doing something nefarious or things have gone very wrong assert id_info['iss'] in { 'accounts.google.com', 'https://accounts.google.com' }, 'wrong google iss' assert id_info['email_verified'], 'google email not verified' # TODO image return { 'email': id_info['email'].lower(), 'first_name': id_info.get('given_name'), 'last_name': id_info.get('family_name'), }
async def check_grecaptcha(m: GrecaptchaModel, request, *, error_headers=None): settings: Settings = request.app['settings'] client_ip = get_ip(request) if not m.grecaptcha_token: logger.warning('grecaptcha not provided, path="%s" ip=%s', request.path, client_ip) raise JsonErrors.HTTPBadRequest(message='No recaptcha value', headers_=error_headers) post_data = { 'secret': settings.grecaptcha_secret, 'response': m.grecaptcha_token, 'remoteip': client_ip, } async with request.app['http_client'].post(settings.grecaptcha_url, data=post_data) as r: if r.status != 200: raise RequestError(r.status, settings.grecaptcha_url, text=await r.text()) data = await r.json() if data['success'] and remove_port(request.host) == data['hostname']: logger.info('grecaptcha success') else: logger.warning('grecaptcha failure, path="%s" ip=%s response=%s', request.path, client_ip, data, extra={'data': { 'grecaptcha_response': data }}) raise JsonErrors.HTTPBadRequest(message='Invalid recaptcha value', headers_=error_headers)
async def facebook_get_details(m: FacebookSiwModel, app): try: sig, data = m.signed_request.split(b'.', 1) except ValueError: raise JsonErrors.HTTPBadRequest( message='"signedRequest" not correctly formed') settings: Settings = app['settings'] expected_sig = hmac.new(settings.facebook_siw_app_secret, data, hashlib.sha256).digest() if not compare_digest(padded_urlsafe_b64decode(sig), expected_sig): raise JsonErrors.HTTPBadRequest( message='"signedRequest" not correctly signed') signed_data = json.loads(padded_urlsafe_b64decode(data).decode()) # can add 'picture' here, but it seems to be low res. details_url = (settings.facebook_siw_url + '?' + urlencode({ 'access_token': m.access_token, 'fields': ['email', 'first_name', 'last_name'] })) async with app['http_client'].get(details_url) as r: if r.status != 200: raise RequestError(r.status, details_url, text=await r.text()) response_data = await r.json() if not (response_data['id'] == signed_data['user_id'] == m.user_id): raise JsonErrors.HTTPBadRequest( message='facebook userID not consistent') if not response_data.get('email') or not response_data.get('last_name'): raise JsonErrors.HTTPBadRequest( message= 'Your Facebook profile needs to have both a last name and email address associated with it.' ) return { 'email': response_data['email'].lower(), 'first_name': response_data['first_name'], 'last_name': response_data['last_name'], }
async def _request(self, method, path, *, idempotency_key=None, **data): post = {} for k, v in data.items(): if isinstance(v, dict): post.update({f'{k}[{kk}]': str(vv) for kk, vv in v.items()}) else: post[k] = str(v) headers = {'Stripe-Version': self._settings.stripe_api_version} if idempotency_key: headers[ 'Idempotency-Key'] = idempotency_key + self._settings.stripe_idempotency_extra full_path = self._settings.stripe_root_url + path async with self._client.request(method, full_path, data=post or None, auth=self._auth, headers=headers) as r: if r.status == 200: return await r.json() else: # check stripe > developer > logs for more info text = await r.text() raise RequestError(r.status, full_path, text=text)
async def check_grecaptcha(m: GrecaptchaModel, client_ip, app): if not m.grecaptcha_token: # TODO remove once grecaptcha_token is required raise JsonErrors.HTTPBadRequest(message='grecaptcha token missing') settings: Settings = app['settings'] post_data = { 'secret': settings.grecaptcha_secret, 'response': m.grecaptcha_token, 'remoteip': client_ip, } async with app['http_client'].post(settings.grecaptcha_url, data=post_data) as r: if r.status != 200: raise RequestError(r.status, settings.grecaptcha_url, info=await r.text()) data = await r.json() if not data['success'] or data['score'] < settings.grecaptcha_threshold: logger.warning('grecaptcha failure, ip=%s, response=%s', client_ip, data) raise JsonErrors.HTTPBadRequest(message='Invalid recaptcha value')
def test_request_error(): r = RequestError(500, 'xxx', text='hello') assert str(r) == 'response 500 from "xxx":\nhello' assert r.extra() == 'hello'