Esempio n. 1
0
    def post(self, user_id):

        post_data = request.get_json()
        reset_user_id = post_data.get('user_id')

        if reset_user_id is not None:
            user = User.query.get(reset_user_id)

            if user is None:
                return make_response(
                    jsonify({
                        'message':
                        'No user found for ID: {}'.format(reset_user_id)
                    })), 404

            UserUtils.admin_reset_user_pin(user)

            response_object = {
                'status': 'success',
                'message': 'Successfully reset pin for user.',
                'data': {
                    'user': user_schema.dump(user).data
                }
            }
            return make_response(jsonify(response_object)), 200
        else:
            response_object = {
                'message': 'No user to reset pin for',
            }
            return make_response(jsonify(response_object)), 400
Esempio n. 2
0
    def put(self, user_id):
        user = User.query.get(user_id)
        reset_tfa = request.args.get('reset_tfa', False) == 'true'
        if user is not None:
            if reset_tfa and AccessControl.has_sufficient_tier(
                    g.user.roles, 'ADMIN', 'sempoadmin'):
                user.reset_TFA()
            if reset_tfa and not AccessControl.has_sufficient_tier(
                    g.user.roles, 'ADMIN', 'sempoadmin'):
                response_object = {
                    'status': 'fail',
                    'message':
                    'Only Sempo admininstrators may reset TFA codes.',
                }
                return make_response(jsonify(response_object)), 403

            user.reset_password()

            response_object = {
                'status': 'success',
                'message': 'Successfully reset password for user.',
                'data': {
                    'user': user_schema.dump(user).data
                }
            }
            return make_response(jsonify(response_object)), 200
        else:
            response_object = {
                'message': 'No user to reset password for',
            }
            return make_response(jsonify(response_object)), 400
Esempio n. 3
0
def signin():
    json_data = request.get_json()
    email_or_username = json_data.get("emailOrUsername")
    password = json_data.get("password")
    validate = signin_validator(email_or_username, password)
    """
    signin_validator() method validates user signin credits.
    if validation is a success, it returns three objects:
    True, errors, user_to_login
    obj:1: Bolean True: validation is a success
    obj:2: list of errors. In this case an empty list.
    obj:3: a user object: used to login the user.
    if validation fails, it returns also three objects:
    obj:1: Bolean False: validation is a failure
    obj:2: list of errors.
    obj:3: None
    """
    if not validate[0]:
        return {
            "code": 400,
            "message": "Please review the errors",
            "success": False,
            "errors": validate[1],
        }, 400
    user = validate[2]
    login_user(user, remember=True)
    return {
        "code": 200,
        "message": "successfully logged-in",
        "success": True,
        "user": user_schema.dump(user),
    }, 200
Esempio n. 4
0
    def get(self):

        version = request.args.get('version', 1)

        if str(version) == '2':
            user = g.user

            responseObject = {
                'message': 'Successfully Loaded.',
                'data': {
                    'user': user_schema.dump(user).data,
                }
            }

            return make_response(jsonify(responseObject)), 201

        user = g.user

        if user.is_subadmin or user.is_supervendor:
            # admins and supervendors see all transfers for that transfer account
            transfer_account = user.transfer_account

            transfers_query = CreditTransfer.query.filter(
                or_(
                    CreditTransfer.recipient_transfer_account_id ==
                    transfer_account.id,
                    CreditTransfer.sender_transfer_account_id ==
                    transfer_account.id))

        else:
            # other users only see transfers involving themselves
            transfers_query = CreditTransfer.query.filter(
                or_(CreditTransfer.recipient_user_id == user.id,
                    CreditTransfer.sender_user_id == user.id))

        transfers, total_items, total_pages = paginate_query(
            transfers_query, CreditTransfer)

        responseObject = {
            'message': 'Successfully Loaded.',
            'items': total_items,
            'pages': total_pages,
            'data': {
                'user': old_user_schema.dump(user).data,
                # 'credit_transfers': transfer_list,
            }
        }

        return make_response(jsonify(responseObject)), 201
Esempio n. 5
0
    def put(self, user_id):
        put_data = request.get_json()

        first_name = put_data.get('first_name')
        last_name = put_data.get('last_name')

        email = put_data.get('email')
        phone = put_data.get('phone')
        public_serial_number = put_data.get('public_serial_number')
        location = put_data.get('location')

        user = User.query.get(user_id)

        if not user:
            response_object = {'message': 'User not found'}
            return make_response(jsonify(response_object)), 400

        if first_name and not first_name == user.first_name:
            user.first_name = first_name

        if last_name and not last_name == user.last_name:
            user.last_name = last_name

        if email and not email == user.email:
            user.email = email

        if phone and not phone == user.phone:
            user.phone = phone

        if public_serial_number and not public_serial_number == user.public_serial_number:
            user.public_serial_number = public_serial_number

        if location and not location == user.location:
            user.location = location

        db.session.commit()

        responseObject = {
            'status': 'success',
            'message': 'Successfully Edited User.',
            'data': {
                'user': user_schema.dump(user).data
            }
        }

        return make_response(jsonify(responseObject)), 201
Esempio n. 6
0
def user_is_authenticated(id):
    if id:
        if current_user.is_authenticated:
            if current_user.id == int(id):
                return {
                    "code": 200,
                    "authenticated": True,
                    "message": "User is logged-in",
                    "success": True,
                    "user": user_schema.dump(current_user),
                }, 200
    return {
        "code": 400,
        "authenticated": False,
        "message": "Either user id missing or you are not authenticated.",
        "success": False,
        "user": None
    }, 400
Esempio n. 7
0
    def get(self):

        user = g.user

        serialised_data = user_schema.dump(user).data

        # TODO: Create a better way of having multiple dummy transfer accounts for testing
        # import copy
        # new_ta = copy.deepcopy(serialised_data['transfer_accounts'][0])
        # new_ta['id'] = new_ta['id'] + 1
        # new_ta['balance'] = 99999
        # new_ta['token']['id'] = 2
        # new_ta['token']['symbol'] = 'GOOP'
        #
        # serialised_data['transfer_accounts'].append(new_ta)

        response_object = {
            'message': 'Successfully Loaded.',
            'data': {
                'user': serialised_data
            }
        }

        return make_response(jsonify(response_object)), 201
Esempio n. 8
0
    def get(self, user_id):
        role = None

        if g.user.is_admin:
            role = 'is_admin'

        can_see_full_details = role in ['is_admin']

        if not can_see_full_details:
            public_serial_number = request.args.get('public_serial_number')

            if public_serial_number:
                user = User.query.filter_by(
                    public_serial_number=public_serial_number.strip()).first()

                if user:
                    transfer_account = TransferAccount.query.get(
                        user.transfer_account_id)

                    if transfer_account:
                        response_object = {
                            'message': 'Successfully found transfer account!',
                            'data': {
                                'balance': transfer_account.balance
                            }
                        }

                        return make_response(jsonify(response_object)), 201

                    response_object = {
                        'message':
                        'No transfer_account for user: {}'.format(user),
                    }

                    return make_response(jsonify(response_object)), 400

                response_object = {
                    'message':
                    'No user for public serial number: {}'.format(
                        public_serial_number),
                }

                return make_response(jsonify(response_object)), 400

            response_object = {
                'message': 'No public_serial_number provided',
            }

            return make_response(jsonify(response_object)), 400

        account_type_filter = request.args.get('account_type')
        if account_type_filter:
            account_type_filter = account_type_filter.lower()

        if user_id:
            user = User.query.get(user_id)

            if user is None:
                response_object = {
                    'message': 'No such user: {}'.format(user_id),
                }

                return make_response(jsonify(response_object)), 400

            response_object = {
                'status': 'success',
                'message': 'Successfully Loaded.',
                'data': {
                    'user': user_schema.dump(user).data
                }
            }

            return make_response(jsonify(response_object)), 201

        else:
            if account_type_filter == 'beneficiary':
                user_query = User.query.filter(User.is_beneficiary)

            elif account_type_filter == 'vendor':
                user_query = User.query.filter(User.is_vendor)

            elif account_type_filter == 'admin':
                user_query = User.query.filter(User.is_subadmin).order_by(
                    User.created.desc())

            else:
                user_query = User.query

            users, total_items, total_pages = paginate_query(user_query, User)

            if users is None:
                response_object = {
                    'message': 'No users',
                }

                return make_response(jsonify(response_object)), 400

            user_list = users_schema.dump(users).data

            response_object = {
                'message': 'Successfully Loaded.',
                'pages': total_pages,
                'items': total_items,
                'data': {
                    'users': user_list,
                }
            }
            return make_response(jsonify(response_object)), 201
Esempio n. 9
0
def proccess_create_or_modify_user_request(attribute_dict,
                                           organisation=None,
                                           allow_existing_user_modify=False,
                                           is_self_sign_up=False,
                                           modify_only=False):
    """
    Takes a create or modify user request and determines the response. Normally what's in the top level API function,
    but here it's one layer down because there's multiple entry points for 'create user':
    - The admin api
    - The register api

    :param attribute_dict: attributes that can be supplied by the request maker
    :param organisation:  what organisation the request maker belongs to. The created user is bound to the same org
    :param allow_existing_user_modify: whether to return an error when the user already exists for the supplied IDs
    :param is_self_sign_up: does the request come from the register api?
    :param modify_only: whether to allow the creation of a  new user
    :return: An http response
    """

    if not attribute_dict.get('custom_attributes'):
        attribute_dict['custom_attributes'] = {}

    user_id = attribute_dict.get('user_id')

    email = attribute_dict.get('email')
    phone = attribute_dict.get('phone')

    account_types = attribute_dict.get('account_types', [])
    if isinstance(account_types, str):
        account_types = account_types.split(',')

    referred_by = attribute_dict.get('referred_by')

    blockchain_address = attribute_dict.get('blockchain_address')

    provided_public_serial_number = attribute_dict.get('public_serial_number')

    uuid = attribute_dict.get('uuid')

    require_identifier = attribute_dict.get('require_identifier', True)

    if not user_id:
        # Extract ID from Combined User ID and Name String if it exists
        try:
            user_id_name_string = attribute_dict.get('user_id_name_string')

            user_id_str = user_id_name_string and user_id_name_string.split(
                ':')[0]

            if user_id_str:
                user_id = int(user_id_str)

        except SyntaxError:
            pass

    if not blockchain_address and provided_public_serial_number:

        try:
            blockchain_address = to_checksum_address(
                provided_public_serial_number)

            # Since it's actually an ethereum address set the provided public serial number to None
            # so it doesn't get used as a transfer card
            provided_public_serial_number = None
        except Exception:
            pass

    require_transfer_card_exists = attribute_dict.get(
        'require_transfer_card_exists',
        g.active_organisation.require_transfer_card)

    public_serial_number = (provided_public_serial_number
                            or attribute_dict.get('payment_card_qr_code')
                            or attribute_dict.get('payment_card_barcode'))

    location = attribute_dict.get('location')  # address location

    # Yes, we know "GPS" refers to a technology, but "gps_location" is less ambiguous for end users than "geo_location"
    gps_location = attribute_dict.get(
        'gps_location')  # geo location as str of lat, lng

    use_precreated_pin = attribute_dict.get('use_precreated_pin')
    use_last_4_digits_of_id_as_initial_pin = attribute_dict.get(
        'use_last_4_digits_of_id_as_initial_pin')

    transfer_account_name = attribute_dict.get('transfer_account_name')
    first_name = attribute_dict.get('first_name')
    last_name = attribute_dict.get('last_name')

    business_usage_name = attribute_dict.get('business_usage_name')
    business_usage_id = None
    if business_usage_name:
        usage = TransferUsage.find_or_create(business_usage_name)
        business_usage_id = usage.id

    preferred_language = attribute_dict.get('preferred_language')

    primary_user_identifier = attribute_dict.get('primary_user_identifier')
    primary_user_pin = attribute_dict.get('primary_user_pin')

    initial_disbursement = attribute_dict.get('initial_disbursement', None)
    if not account_types:
        account_types = ['beneficiary']
    roles_to_set = []
    for at in account_types:
        if at not in g.active_organisation.valid_roles:
            raise Exception(
                f'{at} not a valid role for this organisation. Please choose one of the following: {g.active_organisation.valid_roles}'
            )
        roles_to_set.append((ASSIGNABLE_TIERS[at], at))

    chain = get_chain()
    if current_app.config['CHAINS'][chain]['IS_USING_BITCOIN']:
        try:
            base58.b58decode_check(blockchain_address)
        except ValueError:
            response_object = {
                'message':
                'Blockchain Address {} Not Valid'.format(blockchain_address)
            }
            return response_object, 400

    if isinstance(phone, bool):
        phone = None

    if phone and not is_self_sign_up:
        # phone has already been parsed if self sign up
        try:
            phone = proccess_phone_number(phone)
        except NumberParseException as e:
            response_object = {'message': 'Invalid Phone Number: ' + str(e)}
            return response_object, 400

    # Work out if there's an existing transfer account to bind to
    existing_transfer_account = None
    if primary_user_identifier:
        primary_user, _ = find_user_from_public_identifier(
            primary_user_identifier)

        if not primary_user or not primary_user.verify_password(
                primary_user_pin):
            response_object = {'message': 'Primary User not Found'}
            return response_object, 400

        if not primary_user.verify_password(primary_user_pin):
            response_object = {'message': 'Invalid PIN for Primary User'}
            return response_object, 400

        primary_user_transfer_account = primary_user.transfer_account

        if not primary_user_transfer_account:
            response_object = {
                'message': 'Primary User has no transfer account'
            }
            return response_object, 400

    if not (phone or email or public_serial_number or blockchain_address
            or user_id or uuid or not require_identifier):
        response_object = {'message': 'Must provide a unique identifier'}
        return response_object, 400

    if use_precreated_pin and not public_serial_number:
        response_object = {
            'message':
            'Must provide public serial number to use a transfer card or pre-created pin'
        }
        return response_object, 400

    if public_serial_number:
        public_serial_number = str(public_serial_number)

        if use_precreated_pin or require_transfer_card_exists:
            transfer_card = TransferCard.query.filter_by(
                public_serial_number=public_serial_number).first()

            if not transfer_card:
                response_object = {'message': 'Transfer card not found'}
                return response_object, 400

    business_usage = None
    if business_usage_id:
        business_usage = TransferUsage.query.get(business_usage_id)
        if not business_usage:
            response_object = {
                'message':
                f'Business Usage not found for id {business_usage_id}'
            }
            return response_object, 400

    referred_by_user, _ = find_user_from_public_identifier(referred_by)

    if referred_by and not referred_by_user:
        response_object = {
            'message':
            f'Referrer user not found for public identifier {referred_by}'
        }
        return response_object, 400

    existing_user, _ = find_user_from_public_identifier(
        email, phone, public_serial_number, blockchain_address, uuid)

    if not existing_user and user_id:
        existing_user = User.query.get(user_id)

    if modify_only and existing_user is None:
        response_object = {'message': 'User not found'}
        return response_object, 404

    if existing_user:
        if not allow_existing_user_modify:
            response_object = {'message': 'User already exists for Identifier'}
            return response_object, 400

        try:

            user = update_transfer_account_user(
                existing_user,
                first_name=first_name,
                last_name=last_name,
                preferred_language=preferred_language,
                phone=phone,
                email=email,
                public_serial_number=public_serial_number,
                use_precreated_pin=use_precreated_pin,
                existing_transfer_account=existing_transfer_account,
                roles=roles_to_set,
                business_usage=business_usage)

            set_location_conditionally(user, location, gps_location)

            if referred_by_user:
                user.referred_by.clear(
                )  # otherwise prior referrals will remain...
                user.referred_by.append(referred_by_user)

            set_custom_attributes(attribute_dict, user)
            flag_modified(user, "custom_attributes")

            db.session.commit()

            response_object = {
                'message': 'User Updated',
                'data': {
                    'user': user_schema.dump(user).data
                }
            }

            return response_object, 200

        except Exception as e:
            response_object = {'message': str(e)}

            return response_object, 400

    user = create_transfer_account_user(
        first_name=first_name,
        last_name=last_name,
        preferred_language=preferred_language,
        phone=phone,
        email=email,
        public_serial_number=public_serial_number,
        uuid=uuid,
        organisation=organisation,
        blockchain_address=blockchain_address,
        transfer_account_name=transfer_account_name,
        use_precreated_pin=use_precreated_pin,
        use_last_4_digits_of_id_as_initial_pin=
        use_last_4_digits_of_id_as_initial_pin,
        existing_transfer_account=existing_transfer_account,
        roles=roles_to_set,
        is_self_sign_up=is_self_sign_up,
        business_usage=business_usage,
        initial_disbursement=initial_disbursement)

    set_location_conditionally(user, location, gps_location)

    if referred_by_user:
        user.referred_by.append(referred_by_user)

    if attribute_dict.get('gender'):
        attribute_dict['custom_attributes']['gender'] = attribute_dict.get(
            'gender')

    if attribute_dict.get('bio'):
        attribute_dict['custom_attributes']['bio'] = attribute_dict.get('bio')

    set_custom_attributes(attribute_dict, user)

    if is_self_sign_up and attribute_dict.get('deviceInfo', None) is not None:
        save_device_info(device_info=attribute_dict.get('deviceInfo'),
                         user=user)
    send_onboarding_sms_messages(user)
    # Location fires an async task that needs to know user ID
    db.session.flush()

    if phone:
        if is_self_sign_up:
            send_one_time_code(phone=phone, user=user)
            return {
                'message': 'User Created. Please verify phone number.',
                'otp_verify': True
            }, 200

        elif current_app.config['ONBOARDING_SMS']:
            try:
                send_onboarding_sms_messages(user)
            except Exception as e:
                print(e)
                sentry_sdk.capture_exception(e)
                pass

    response_object = {
        'message': 'User Created',
        'data': {
            'user': user_schema.dump(user).data
        }
    }

    return response_object, 200
Esempio n. 10
0
    def get(self, user_id):

        can_see_full_details = AccessControl.has_suffient_role(
            g.user.roles, {'ADMIN': 'admin'})

        if not can_see_full_details:
            public_serial_number = request.args.get('public_serial_number')

            if public_serial_number:
                user = User.query.filter_by(
                    public_serial_number=public_serial_number.strip()).first()

                if user:

                    if user.default_transfer_account:
                        response_object = {
                            'message': 'Successfully found transfer account!',
                            'data': {
                                'balance':
                                user.default_transfer_account.balance
                            }
                        }

                        return make_response(jsonify(response_object)), 201

                    response_object = {
                        'message':
                        'No transfer_account for user: {}'.format(user),
                    }

                    return make_response(jsonify(response_object)), 400

                response_object = {
                    'message':
                    'No user for public serial number: {}'.format(
                        public_serial_number),
                }

                return make_response(jsonify(response_object)), 400

            response_object = {
                'message': 'No public_serial_number provided',
            }

            return make_response(jsonify(response_object)), 400

        account_type_filter = request.args.get('account_type')
        if account_type_filter:
            account_type_filter = account_type_filter.lower()

        if user_id:
            user = User.query.get(user_id)
            #
            # user.cashout_authorised()

            if user is None:
                response_object = {
                    'message': 'No such user: {}'.format(user_id),
                }

                return make_response(jsonify(response_object)), 400

            response_object = {
                'status': 'success',
                'message': 'Successfully Loaded.',
                'data': {
                    'user': user_schema.dump(user).data
                }
            }

            return make_response(jsonify(response_object)), 200

        else:
            if account_type_filter == 'beneficiary':
                user_query = User.query.filter(User.has_beneficiary_role)

            elif account_type_filter == 'vendor':
                user_query = User.query.filter(User.has_vendor_role)

            elif account_type_filter == 'admin':
                user_query = User.query.filter(User.has_admin_role).order_by(
                    User.created.desc())

            else:
                user_query = User.query

            users, total_items, total_pages = paginate_query(user_query, User)

            if users is None:
                response_object = {
                    'message': 'No users',
                }

                return make_response(jsonify(response_object)), 400

            user_list = users_schema.dump(users).data

            response_object = {
                'message': 'Successfully Loaded.',
                'pages': total_pages,
                'items': total_items,
                'data': {
                    'users': user_list,
                }
            }
            return make_response(jsonify(response_object)), 200
Esempio n. 11
0
def proccess_create_or_modify_user_request(
    attribute_dict,
    organisation=None,
    allow_existing_user_modify=False,
    is_self_sign_up=False,
    modify_only=False,
):
    """
    Takes a create or modify user request and determines the response. Normally what's in the top level API function,
    but here it's one layer down because there's multiple entry points for 'create user':
    - The admin api
    - The register api

    :param attribute_dict: attributes that can be supplied by the request maker
    :param organisation:  what organisation the request maker belongs to. The created user is bound to the same org
    :param allow_existing_user_modify: whether to return and error when the user already exists for the supplied IDs
    :param is_self_sign_up: does the request come from the register api?
    :return: An http response
    """

    if not attribute_dict.get('custom_attributes'):
        attribute_dict['custom_attributes'] = {}

    user_id = attribute_dict.get('user_id')

    email = attribute_dict.get('email')
    phone = attribute_dict.get('phone')

    referred_by = attribute_dict.get('referred_by')

    blockchain_address = attribute_dict.get('blockchain_address')

    provided_public_serial_number = attribute_dict.get('public_serial_number')

    if not blockchain_address and provided_public_serial_number:

        try:
            blockchain_address = to_checksum_address(
                provided_public_serial_number)

            # Since it's actually an ethereum address set the provided public serial number to None
            # so it doesn't get used as a transfer card
            provided_public_serial_number = None
        except Exception:
            pass

    require_transfer_card_exists = attribute_dict.get(
        'require_transfer_card_exists',
        g.active_organisation.require_transfer_card)

    public_serial_number = (provided_public_serial_number
                            or attribute_dict.get('payment_card_qr_code')
                            or attribute_dict.get('payment_card_barcode'))

    location = attribute_dict.get('location')  # address location
    geo_location = attribute_dict.get(
        'geo_location')  # geo location as str of lat, lng

    if geo_location:
        geo = geo_location.split(' ')
        lat = geo[0]
        lng = geo[1]
    else:
        # TODO: Work out how this passed tests when this wasn't definied properly!?!
        lat = None
        lng = None

    use_precreated_pin = attribute_dict.get('use_precreated_pin')
    use_last_4_digits_of_id_as_initial_pin = attribute_dict.get(
        'use_last_4_digits_of_id_as_initial_pin')

    transfer_account_name = attribute_dict.get('transfer_account_name')
    first_name = attribute_dict.get('first_name')
    last_name = attribute_dict.get('last_name')

    business_usage_name = attribute_dict.get('business_usage_name')
    business_usage_id = None
    if business_usage_name:
        usage = TransferUsage.find_or_create(business_usage_name)
        business_usage_id = usage.id

    preferred_language = attribute_dict.get('preferred_language')

    primary_user_identifier = attribute_dict.get('primary_user_identifier')
    primary_user_pin = attribute_dict.get('primary_user_pin')

    initial_disbursement = attribute_dict.get('initial_disbursement', None)

    is_vendor = attribute_dict.get('is_vendor', None)
    if is_vendor is None:
        is_vendor = attribute_dict.get('vendor', False)

    is_tokenagent = attribute_dict.get('is_tokenagent', False)
    is_groupaccount = attribute_dict.get('is_groupaccount', False)

    # is_beneficiary defaults to the opposite of is_vendor
    is_beneficiary = attribute_dict.get(
        'is_beneficiary', not is_vendor and not is_tokenagent
        and not is_groupaccount)

    if current_app.config['IS_USING_BITCOIN']:
        try:
            base58.b58decode_check(blockchain_address)
        except ValueError:
            response_object = {
                'message':
                'Blockchain Address {} Not Valid'.format(blockchain_address)
            }
            return response_object, 400

    if isinstance(phone, bool):
        phone = None

    if phone and not is_self_sign_up:
        # phone has already been parsed if self sign up
        try:
            phone = proccess_phone_number(phone)
        except NumberParseException as e:
            response_object = {'message': 'Invalid Phone Number: ' + str(e)}
            return response_object, 400

    # Work out if there's an existing transfer account to bind to
    existing_transfer_account = None
    if primary_user_identifier:
        primary_user = find_user_from_public_identifier(
            primary_user_identifier)

        if not primary_user or not primary_user.verify_password(
                primary_user_pin):
            response_object = {'message': 'Primary User not Found'}
            return response_object, 400

        if not primary_user.verify_password(primary_user_pin):
            response_object = {'message': 'Invalid PIN for Primary User'}
            return response_object, 400

        primary_user_transfer_account = primary_user.transfer_account

        if not primary_user_transfer_account:
            response_object = {
                'message': 'Primary User has no transfer account'
            }
            return response_object, 400

    if not (phone or email or public_serial_number or blockchain_address):
        response_object = {'message': 'Must provide a unique identifier'}
        return response_object, 400

    if use_precreated_pin and not public_serial_number:
        response_object = {
            'message':
            'Must provide public serial number to use a transfer card or pre-created pin'
        }
        return response_object, 400

    if public_serial_number:
        public_serial_number = str(public_serial_number)

        if use_precreated_pin or require_transfer_card_exists:
            transfer_card = TransferCard.query.filter_by(
                public_serial_number=public_serial_number).first()

            if not transfer_card:
                response_object = {'message': 'Transfer card not found'}
                return response_object, 400

    business_usage = None
    if business_usage_id:
        business_usage = TransferUsage.query.get(business_usage_id)
        if not business_usage:
            response_object = {
                'message':
                f'Business Usage not found for id {business_usage_id}'
            }
            return response_object, 400

    referred_by_user = find_user_from_public_identifier(referred_by)

    if referred_by and not referred_by_user:
        response_object = {
            'message':
            f'Referrer user not found for public identifier {referred_by}'
        }
        return response_object, 400

    existing_user = find_user_from_public_identifier(email, phone,
                                                     public_serial_number,
                                                     blockchain_address)

    if modify_only:
        existing_user = User.query.get(user_id)

    if modify_only and existing_user is None:
        response_object = {'message': 'User not found'}
        return response_object, 404

    if existing_user:
        if not allow_existing_user_modify:
            response_object = {'message': 'User already exists for Identifier'}
            return response_object, 400

        try:

            user = update_transfer_account_user(
                existing_user,
                first_name=first_name,
                last_name=last_name,
                preferred_language=preferred_language,
                phone=phone,
                email=email,
                location=location,
                public_serial_number=public_serial_number,
                use_precreated_pin=use_precreated_pin,
                existing_transfer_account=existing_transfer_account,
                is_beneficiary=is_beneficiary,
                is_vendor=is_vendor,
                is_tokenagent=is_tokenagent,
                is_groupaccount=is_groupaccount,
                business_usage=business_usage)

            if referred_by_user:
                user.referred_by.clear(
                )  # otherwise prior referrals will remain...
                user.referred_by.append(referred_by_user)

            set_custom_attributes(attribute_dict, user)
            flag_modified(user, "custom_attributes")

            db.session.commit()

            response_object = {
                'message': 'User Updated',
                'data': {
                    'user': user_schema.dump(user).data
                }
            }

            return response_object, 200

        except Exception as e:
            response_object = {'message': str(e)}

            return response_object, 400

    user = create_transfer_account_user(
        first_name=first_name,
        last_name=last_name,
        preferred_language=preferred_language,
        phone=phone,
        email=email,
        public_serial_number=public_serial_number,
        organisation=organisation,
        blockchain_address=blockchain_address,
        transfer_account_name=transfer_account_name,
        lat=lat,
        lng=lng,
        use_precreated_pin=use_precreated_pin,
        use_last_4_digits_of_id_as_initial_pin=
        use_last_4_digits_of_id_as_initial_pin,
        existing_transfer_account=existing_transfer_account,
        is_beneficiary=is_beneficiary,
        is_vendor=is_vendor,
        is_tokenagent=is_tokenagent,
        is_groupaccount=is_groupaccount,
        is_self_sign_up=is_self_sign_up,
        business_usage=business_usage,
        initial_disbursement=initial_disbursement)

    if referred_by_user:
        user.referred_by.append(referred_by_user)

    if attribute_dict.get('gender'):
        attribute_dict['custom_attributes']['gender'] = attribute_dict.get(
            'gender')

    if attribute_dict.get('bio'):
        attribute_dict['custom_attributes']['bio'] = attribute_dict.get('bio')

    set_custom_attributes(attribute_dict, user)

    if is_self_sign_up and attribute_dict.get('deviceInfo', None) is not None:
        save_device_info(device_info=attribute_dict.get('deviceInfo'),
                         user=user)
    send_onboarding_sms_messages(user)
    # Location fires an async task that needs to know user ID
    db.session.flush()

    if location:
        user.location = location

    if phone:
        if is_self_sign_up:
            send_one_time_code(phone=phone, user=user)
            return {
                'message': 'User Created. Please verify phone number.',
                'otp_verify': True
            }, 200

        elif current_app.config['ONBOARDING_SMS']:
            try:
                send_onboarding_sms_messages(user)
            except Exception as e:
                print(e)
                sentry_sdk.capture_exception(e)
                pass

    response_object = {
        'message': 'User Created',
        'data': {
            'user': user_schema.dump(user).data
        }
    }

    return response_object, 200
Esempio n. 12
0
def proccess_attribute_dict(attribute_dict,
                            force_dict_keys_lowercase=False,
                            allow_existing_user_modify=False,
                            require_transfer_card_exists=False):
    elapsed_time('1.0 Start')

    if force_dict_keys_lowercase:
        attribute_dict = force_attribute_dict_keys_to_lowercase(attribute_dict)

    attribute_dict = strip_kobo_preslashes(attribute_dict)

    attribute_dict = apply_settings(attribute_dict)

    attribute_dict = truthy_all_dict_values(attribute_dict)

    attribute_dict = strip_whitespace_characters(attribute_dict)

    elapsed_time('2.0 Post Processing')

    email = attribute_dict.get('email')
    phone = attribute_dict.get('phone')

    blockchain_address = attribute_dict.get('blockchain_address')

    provided_public_serial_number = attribute_dict.get('public_serial_number')

    if not blockchain_address and provided_public_serial_number:

        try:
            blockchain_address = utils.checksum_encode(
                provided_public_serial_number)

            # Since it's actually an ethereum address set the provided public serial number to None
            # so it doesn't get used as a transfer card
            provided_public_serial_number = None
        except Exception:
            pass

    public_serial_number = (provided_public_serial_number
                            or attribute_dict.get('payment_card_qr_code')
                            or attribute_dict.get('payment_card_barcode'))

    location = attribute_dict.get('location')

    use_precreated_pin = attribute_dict.get('use_precreated_pin')
    use_last_4_digits_of_id_as_initial_pin = attribute_dict.get(
        'use_last_4_digits_of_id_as_initial_pin')

    transfer_account_name = attribute_dict.get('transfer_account_name')
    first_name = attribute_dict.get('first_name')
    last_name = attribute_dict.get('last_name')

    primary_user_identifier = attribute_dict.get('primary_user_identifier')
    primary_user_pin = attribute_dict.get('primary_user_pin')

    custom_initial_disbursement = attribute_dict.get(
        'custom_initial_disbursement', None)

    is_vendor = attribute_dict.get('is_vendor', None)
    if is_vendor is None:
        is_vendor = attribute_dict.get('vendor', False)

    # is_beneficiary defaults to the opposite of is_vendor
    is_beneficiary = attribute_dict.get('is_beneficiary', not is_vendor)

    if current_app.config['IS_USING_BITCOIN']:
        try:
            base58.b58decode_check(blockchain_address)
        except ValueError:
            response_object = {
                'message':
                'Blockchain Address {} Not Valid'.format(blockchain_address)
            }
            return response_object, 400

    if isinstance(phone, bool):
        phone = None

    if phone:
        try:
            phone = proccess_phone_number(phone)
        except NumberParseException as e:
            response_object = {'message': 'Invalid Phone Number: ' + str(e)}
            return response_object, 400

    # Work out if there's an existing transfer account to bind to
    existing_transfer_account = None
    if primary_user_identifier:

        primary_user = find_user_from_public_identifier(
            primary_user_identifier)

        if not primary_user or not primary_user.verify_password(
                primary_user_pin):
            response_object = {'message': 'Primary User not Found'}
            return response_object, 400

        if not primary_user.verify_password(primary_user_pin):

            response_object = {'message': 'Invalid PIN for Primary User'}
            return response_object, 400

        primary_user_transfer_account = primary_user.transfer_account

        if not primary_user_transfer_account:
            response_object = {
                'message': 'Primary User has no transfer account'
            }
            return response_object, 400

    if not (phone or email or public_serial_number or blockchain_address):
        response_object = {'message': 'Must provide a unique identifier'}
        return response_object, 400

    if use_precreated_pin and not public_serial_number:
        response_object = {
            'message':
            'Must provide public serial number to use a transfer card or pre-created pin'
        }
        return response_object, 400

    if public_serial_number:
        public_serial_number = str(public_serial_number)

        if use_precreated_pin or require_transfer_card_exists:
            transfer_card = models.TransferCard.query.filter_by(
                public_serial_number=public_serial_number).first()

            if not transfer_card:
                response_object = {'message': 'Transfer card not found'}
                return response_object, 400

    if custom_initial_disbursement and not custom_initial_disbursement <= current_app.config[
            'MAXIMUM_CUSTOM_INITIAL_DISBURSEMENT']:
        response_object = {
            'message':
            'Disbursement more than maximum allowed amount ({} {})'.format(
                current_app.config['MAXIMUM_CUSTOM_INITIAL_DISBURSEMENT'] /
                100, current_app.config['CURRENCY_NAME'])
        }
        return response_object, 400

    existing_user = find_user_from_public_identifier(email, phone,
                                                     public_serial_number,
                                                     blockchain_address)
    if existing_user:

        if not allow_existing_user_modify:
            response_object = {'message': 'User already exists for Identifier'}
            return response_object, 400

        user = update_transfer_account_user(
            existing_user,
            first_name=first_name,
            last_name=last_name,
            phone=phone,
            email=email,
            public_serial_number=public_serial_number,
            use_precreated_pin=use_precreated_pin,
            existing_transfer_account=existing_transfer_account,
            is_beneficiary=is_beneficiary,
            is_vendor=is_vendor)

        default_attributes, custom_attributes = set_custom_attributes(
            attribute_dict, user)
        flag_modified(user, "custom_attributes")

        db.session.commit()

        response_object = {
            'message': 'User Updated',
            'data': {
                'user': user_schema.dump(user).data
            }
        }

        return response_object, 200

    elapsed_time('3.0 Ready to create')

    user = create_transfer_account_user(
        first_name=first_name,
        last_name=last_name,
        phone=phone,
        email=email,
        public_serial_number=public_serial_number,
        blockchain_address=blockchain_address,
        transfer_account_name=transfer_account_name,
        location=location,
        use_precreated_pin=use_precreated_pin,
        use_last_4_digits_of_id_as_initial_pin=
        use_last_4_digits_of_id_as_initial_pin,
        existing_transfer_account=existing_transfer_account,
        is_beneficiary=is_beneficiary,
        is_vendor=is_vendor)

    elapsed_time('4.0 Created')

    default_attributes, custom_attributes = set_custom_attributes(
        attribute_dict, user)

    if custom_initial_disbursement:
        try:
            disbursement = CreditTransferUtils.make_disbursement_transfer(
                custom_initial_disbursement, user)
        except Exception as e:
            response_object = {'message': str(e)}
            return response_object, 400

    elapsed_time('5.0 Disbursement done')

    db.session.flush()

    if location:
        user.location = location

    if phone and current_app.config['ONBOARDING_SMS']:
        try:
            balance = user.transfer_account.balance
            if isinstance(balance, int):
                balance = balance / 100

            send_onboarding_message(first_name=user.first_name,
                                    to_phone=phone,
                                    credits=balance,
                                    one_time_code=user.one_time_code)
        except Exception as e:
            print(e)
            pass

    if user.one_time_code:
        response_object = {
            'message': 'User Created',
            'data': {
                'user': user_schema.dump(user).data
            }
        }

    else:
        response_object = {
            'message': 'User Created',
            'data': {
                'user': user_schema.dump(user).data
            }
        }

    elapsed_time('6.0 Complete')

    return response_object, 200