def user_phone_to_object(phonenumber, region=None): """ Converts a user provided phone number to a phone object :param phonenumber: :return: """ try: x = phonenumbers.parse(phonenumber) except phonenumbers.NumberParseException: log.warn("Failed to parse user provided number") else: return x if not region: log.debug("Could not parse %s", phonenumber) return None log.debug("Parsing %s w/ region", phonenumber) try: x = phonenumbers.parse(phonenumber, region=region) except phonenumbers.NumberParseException: log.warn("Failed to parse user provided number w/ region") else: return x return None
def authenticate_or_confirm_user(user_info): """ this method takes care of confiriming the users and nullifying the otp_info custom attribute :param user_info: :return: """ idp_pool_id = get_mandatory_evar('COGNITO_IDP_POOL_ID') log.debug("Checking the user confirmation status") if user_info['UserStatus'] == 'UNCONFIRMED': log.info("Confirming the user: {} ".format(user_info['Username'])) response = idp.admin_confirm_sign_up(UserPoolId=idp_pool_id, Username=user_info['Username']) log.info("Signup confirm response: {} ".format(response)) # TODO: How do we mark the phone as validated? # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp.html#CognitoIdentityProvider.Client.verify_user_attribute # resetting the otp_info custom attribute response = idp.admin_update_user_attributes(UserPoolId=idp_pool_id, Username=user_info['Username'], UserAttributes=[{ 'Name': 'custom:otp_info', 'Value': '' }]) log.debug("Custom Attribute update response:{}".format(response))
def get_userinfo_by_phone(phoneno): idp_pool_id = get_mandatory_evar('COGNITO_IDP_POOL_ID') filter = "phone_number=\"{}\"".format(phoneno) response = idp.list_users(UserPoolId=idp_pool_id, Filter=filter) log.debug("List user response for filter {} is {}".format( filter, str(response))) if len(response['Users']) > 0: return response['Users'][0] return None
def verify(event, context): log.debug("Received event: %s", json.dumps(event)) try: event = json.loads(event["body"]) except ValueError: return { 'statusCode': 403, 'headers': headers, 'body': json.dumps({"reason": "Bad json in body"}) } try: code = get_mandatory_event_attr(event, 'code') original_phone_number = get_mandatory_event_attr(event, 'phonenumber') except ValueError, ve: log.error("Missing Code or phonenumber") return { 'statusCode': 403, 'headers': headers, 'body': json.dumps({"reason": "Missing code or phonenumber"}) }
def get_access_token(user_name): """ this method logs in as the user using standard password and generates the jwt tokens :param user_name: :return: """ idp_pool_id = get_mandatory_evar('COGNITO_IDP_POOL_ID') idp_pool_client_id = get_mandatory_evar('COGNITO_IDP_POOL_CLIENT_ID') password = get_password() auth_response = idp.admin_initiate_auth(UserPoolId=idp_pool_id, ClientId=idp_pool_client_id, AuthFlow='ADMIN_NO_SRP_AUTH', AuthParameters={ 'USERNAME': user_name, 'PASSWORD': password }) log.debug( " Admin initiate auth is successful for user {} ".format(user_name)) return auth_response['AuthenticationResult']
return password # Getting the user attributes if the user already signedup. def get_userinfo_by_phone(phoneno): idp_pool_id = get_mandatory_evar('COGNITO_IDP_POOL_ID') filter = "phone_number=\"{}\"".format(phoneno) try: response = idp.list_users(UserPoolId=idp_pool_id, Filter=filter) except Exception, e: log.exception("Failed looking up list user (type %s)" % type(e)) return None log.debug("List user response for filter {} is {}".format( filter, str(response))) if response.get('ResponseMetadata', {}).get('HTTPStatusCode') not in (200, 201, 202): log.error("Failed to look up user successfully") return None users = response.get('Users') if not users: log.error("Failed retrieving users: %s", response) return None if len(users) == 0: log.error("No users found") return None
def send(event, context): idp_pool_id = get_mandatory_evar('COGNITO_IDP_POOL_ID') idp_pool_client_id = get_mandatory_evar('COGNITO_IDP_POOL_CLIENT_ID') log.debug("Event %s", event) if 'body' not in event: return {'statusCode': BAD_REQUEST, 'body': {'reason': "body"}} try: single_event = json.loads(event['body']) except TypeError: log.info("Failed loading body type=%s", type(event['body'])) if isinstance(event['body'], dict): single_event = event['body'] else: return { 'statusCode': BAD_REQUEST, 'body': { 'reason': 'Bad Body: \"%s\"' % event.get('body') } } original_phone_number = single_event.get('phonenumber') if not original_phone_number: return { 'statusCode': BAD_REQUEST, 'body': { 'reason': "missing phonenumber" } } phone_object = user_phone_to_object(original_phone_number) if not phone_object: # TODO: Base country code off the country code of calling country, agent-terminal-id, key, length? phone_object = user_phone_to_object(original_phone_number, region='MX') if not phone_object: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({ "reason": "Bad phonenumber: \"%s\"" % (original_phone_number), }) } phone_number = phonenumbers.format_number( phone_object, phonenumbers.PhoneNumberFormat.E164) #TODO: test the phone number: # https://stackoverflow.com/a/23299989/1257603 # Also as an efficiency and performance measure: post to a queue and # do a bunch of messages at once auth_key = os.environ.get('PARAM_STORE_PRESHARED_KEY') log.debug("Retrieved Key: %s", auth_key) if not auth_key: return { 'statusCode': INTERNAL_SERVER_ERROR, 'body': { 'reason': 'must set preshared key' } } api_path = os.environ.get('api_path', 'ops-alpha/telco/outbound') posting_path = "https://api.athenabitcoin.net/%s" % api_path # Querying the user info before sending the code so that we can reduce the delay between sms code and user creation. user_info = get_userinfo_by_phone(phone_number) rand_code = random.randint(100000, 999999) for lang in ['language', 'lang']: if lang in single_event: break default_lang_code = 'en' #provided in swagger lang_code = single_event.get(lang) countrycode = phonenumbers.region_code_for_number(phone_object) if not lang_code: if countrycode in ('MX', 'CO', 'AR'): lang_code = 'es' else: lang_code = default_lang_code if lang_code == 'en': msg = 'Your code is: %s' % rand_code elif lang_code == 'es': msg = 'Voy a enviarte un código: %s' % rand_code else: log.warn("Bad Language code, %s, defaulting to %s", lang_code, default_lang_code) data = { 'phone_to': phone_number, 'message': msg, 'country': countrycode, 'method': 'sms' } log.debug("Posting to data: %s to %s. Time Remaining: %s", data, posting_path, context.get_remaining_time_in_millis()) try: z = requests.post(posting_path, headers={'Authorization': auth_key}, json=data, timeout=(context.get_remaining_time_in_millis() * .95 / 1000)) except TimeoutException, te: return { 'statusCode': GATEWAY_TIMEOUT, 'body': { 'reason': 'Unexpected delay in processing' } }
}) if user_info: ''' If the user already present in user pool update the custom attribute with the code we sent and time. ''' response = idp.admin_update_user_attributes( UserPoolId=idp_pool_id, Username=user_info['Username'], UserAttributes=[ { 'Name': 'custom:otp_info', 'Value': secret_info }, ]) log.debug("Custom Attribute update response:{}".format(response)) else: ''' if the user not present in our system create the unconfirmed user with the code we sent. ''' response = idp.sign_up(ClientId=idp_pool_client_id, Username=str(phone_number), Password=get_password(), UserAttributes=[{ 'Name': 'phone_number', 'Value': str(phone_number) }, { 'Name': 'custom:otp_info', 'Value': secret_info }]) log.debug("Unconfirmed user creation response:{}".format(response))
def create_transaction(event, context): """ :param event: :param context: :return: """ log.debug("Received event in Create: %s", event) request_body = event['body'] if isinstance(request_body, basestring): try: request_body = json.loads(request_body) except TypeError: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({'message': 'Cannot decode JSON'}) } except ValueError: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({'message': 'Bad request (no body)'}) } elif isinstance(request_body, dict): log.debug("Body type was dict; assuming test request") pass else: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps( {'message': "Bad type for body: %s" % type(request_body)}) } if "address" not in request_body: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({'message': 'No Address'}) } is_good_address = pycoin_validator.is_address_valid( address=request_body['address']) if not is_good_address: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({ 'message': 'Not a valid address: %s' % request_body['address'] }) } try: tx = QPagosPurchase(initial="price_quoted", request_context=event['requestContext'], **request_body) except TxInstantiationException, tie: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': tie.to_json() }
def complete_transaction(event, context): """ :param event: :param context: :return: """ # TODO --> this is a copy. We should wrap these in a decorator & reduce the code. log.debug("Received event in Complete: %s", event) request_body = event['body'] if isinstance(request_body, basestring): try: request_body = json.loads(request_body) except TypeError: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({'message': 'Cannot decode JSON'}) } except ValueError: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({'message': 'Bad request (no body)'}) } elif isinstance(request_body, dict): log.debug("Body type was dict; assuming test request") pass else: return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps( {'message': "Bad type for body: %s" % type(request_body)}) } #### Copied over required_fields = ['uuid', "amount_deposited"] for f in required_fields: if f in request_body: continue return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({'message': "Request needs %s" % f}) } storage = QPagosPurchase.get_storage() persistent_key = request_body['uuid'] log.debug("Looking for key=%s in %s", persistent_key, storage) try: order = storage.retrieve(persistent_key) except Exception, e: log.exception("Failed retrieving %s from %s", persistent_key, storage) return { 'statusCode': BAD_REQUEST, 'headers': headers, 'body': json.dumps({'message': str(e)}) }
# excepthook type error that I could not figure out in a timely manner airbrake_except_hook = sys.excepthook sys.excepthook = original_except_hook try: tx.submit() finally: sys.excepthook = airbrake_except_hook tx_as_dict = tx.as_dict() try: tx_json = json.dumps(tx_as_dict, default=default_encode) except (TypeError, ValueError), e: log.exception("Failed to create tx as json") try: log.debug("Dictionary failed to convert: %s", tx_as_dict) except: log.warn("Could not even convert to dict") finally: return { 'statusCode': INTERNAL_SERVER_ERROR, 'body': { "message": "Object failed to serialize to json (%s)" % e }, 'headers': headers } else: return {'statusCode': OK, 'body': tx_json, 'headers': headers} def complete_transaction(event, context):