Exemple #1
0
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
Exemple #2
0
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
Exemple #4
0
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"})
        }
Exemple #5
0
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']
Exemple #6
0
    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))
Exemple #9
0
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()
        }
Exemple #10
0
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)})
        }
Exemple #11
0
    #   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):